java百萬(wàn)查詢(xún)語(yǔ)句優(yōu)化
業(yè)務(wù)需求
今天去面試時(shí)hr問(wèn)了個(gè)關(guān)于大量數(shù)據(jù)查詢(xún)的問(wèn)題。
面試官:“我們公司是做數(shù)據(jù)分析的,每次需要從數(shù)據(jù)庫(kù)中查詢(xún)100萬(wàn)條數(shù)據(jù)進(jìn)行分析,該接口不能用分頁(yè)(不限制具體怎么實(shí)現(xiàn)),請(qǐng)問(wèn)怎么優(yōu)化sql或者java代碼呢??”
如果用普通查詢(xún)需要5分多分鐘才查詢(xún)完畢,所以我們用索引加多線(xiàn)程來(lái)實(shí)現(xiàn)。
那我們就開(kāi)始吧!GO??!GO!!
數(shù)據(jù)庫(kù)設(shè)計(jì)
編寫(xiě)數(shù)據(jù)庫(kù)字段
然后要生成100萬(wàn)條數(shù)據(jù)
在數(shù)據(jù)庫(kù)添加索引
索引這個(gè)方面我還是不太了解,大家懂的可以?xún)?yōu)化索引
代碼實(shí)現(xiàn)
java編寫(xiě)
controller類(lèi)編寫(xiě)
package com.neu.controller;
import com.neu.mapper.UserMapper;
import com.neu.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 用戶(hù)查詢(xún)多線(xiàn)程用戶(hù)Controller
* @author 薄荷藍(lán)檸
* @since 2023/6/6
*/
@Controller
public class ExecutorUtils {
@Resource
private UserMapper userMapper;
// 一個(gè)線(xiàn)程最大處理數(shù)據(jù)量
private static final int THREAD_COUNT_SIZE = 5000;
@RequestMapping("Executor")
public List<User> executeThreadPool() {
//計(jì)算表總數(shù)
Integer integer = userMapper.UserSum();
//記錄開(kāi)始時(shí)間
long start = System.currentTimeMillis();
//new個(gè)和表總數(shù)一樣長(zhǎng)的ArrayList
List<User> threadList=new ArrayList<>(integer);
// 線(xiàn)程數(shù),以5000條數(shù)據(jù)為一個(gè)線(xiàn)程,總數(shù)據(jù)大小除以5000,再加1
int round = integer / THREAD_COUNT_SIZE + 1;
//new一個(gè)臨時(shí)儲(chǔ)存List的Map,以線(xiàn)程名為k,用做list排序
Map<Integer,ArrayList> temporaryMap = new HashMap<>(round);
// 程序計(jì)數(shù)器
final CountDownLatch count = new CountDownLatch(round);
// 創(chuàng)建線(xiàn)程
ExecutorService executor = Executors.newFixedThreadPool(round);
// 分配數(shù)據(jù)
for (int i = 0; i < round; i++) {
//該線(xiàn)程的查詢(xún)開(kāi)始值
int startLen = i * THREAD_COUNT_SIZE;
int k = i + 1;
executor.execute(new Runnable() {
@Override
public void run() {
ArrayList<User> users = userMapper.subList(startLen);
//把查出來(lái)的List放進(jìn)臨時(shí)Map
temporaryMap.put(k,users);
System.out.println("正在處理線(xiàn)程【" + k + "】的數(shù)據(jù),數(shù)據(jù)大小為:" + users.size());
// 計(jì)數(shù)器 -1(喚醒阻塞線(xiàn)程)
count.countDown();
}
});
}
try {
// 阻塞線(xiàn)程(主線(xiàn)程等待所有子線(xiàn)程 一起執(zhí)行業(yè)務(wù))
count.await();
//結(jié)束時(shí)間
long end = System.currentTimeMillis();
System.out.println("100萬(wàn)數(shù)據(jù)查詢(xún)耗時(shí):" + (end - start) + "ms");
//通過(guò)循環(huán)遍歷臨時(shí)map,把map的值有序的放進(jìn)List里
temporaryMap.keySet().forEach(k->{
threadList.addAll(temporaryMap.get(k));
});
} catch (Exception e) {
e.printStackTrace();
} finally {
//清除臨時(shí)map,釋放內(nèi)存
temporaryMap.clear();
// 終止線(xiàn)程池
// 啟動(dòng)一次順序關(guān)閉,執(zhí)行以前提交的任務(wù),但不接受新任務(wù)。若已經(jīng)關(guān)閉,則調(diào)用沒(méi)有其他作用。
executor.shutdown();
}
//輸出list的長(zhǎng)度
System.out.println("list長(zhǎng)度為:"+threadList.size());
return threadList;
}
}
編寫(xiě)Mapper
package com.neu.mapper;
import java.util.ArrayList;
import java.util.List;
import org.apache.ibatis.annotations.*;
import com.neu.pojo.User;
/**
* 用戶(hù)查詢(xún)多線(xiàn)程用戶(hù)Controller
* @author 薄荷藍(lán)檸
* @since 2023/6/6
*/
@Mapper
public interface UserMapper {
/**
* 檢索user表的長(zhǎng)度
* @return 表長(zhǎng)度
*/
@Select("SELECT count(id) as sum FROM sysuser")
Integer UserSum();
/**
* 檢索user表的所有記錄,
* SELECT * 無(wú)法使用 MySQL 優(yōu)化器覆蓋索引的優(yōu)化(基于 MySQL 優(yōu)化器的“覆蓋索引”策略又是速度極快,效率極高,業(yè)界極為推薦的查詢(xún)優(yōu)化方式)
* @return 所有記錄信息
*/
@Select("select id,account,password,name,job,rights,roleId,createUserId,createTime,updateUserId,updateTime,del,enable from sysuser LIMIT #{startLen},5000")
ArrayList<User> subList(@Param("startLen") int startLen);
}
編寫(xiě)完成后我們測(cè)試一波–>
測(cè)試結(jié)果20秒內(nèi),比之前快了好多
模糊查詢(xún)
模糊查詢(xún)呢?
咱測(cè)試一下:
修改Mapper
package com.neu.mapper;
import java.util.ArrayList;
import java.util.List;
import org.apache.ibatis.annotations.*;
import com.neu.pojo.User;
/**
* 用戶(hù)查詢(xún)多線(xiàn)程用戶(hù)Controller
* @author 薄荷藍(lán)檸
* @since 2023/6/6
*/
@Mapper
public interface UserMapper {
/**
* 檢索user表id包含有“0”的長(zhǎng)度
* @return 表長(zhǎng)度
*/
@Select("SELECT count(id) as sum FROM sysuser where id like concat('%',0,'%')")
Integer UserSum();
/**
* 檢索user表id包含有“0”的所有記錄
* SELECT * 無(wú)法使用 MySQL 優(yōu)化器覆蓋索引的優(yōu)化(基于 MySQL 優(yōu)化器的“覆蓋索引”策略又是速度極快,效率極高,業(yè)界極為推薦的查詢(xún)優(yōu)化方式)
* @return 所有記錄信息
*/
@Select("select id,account,password,name,job,rights,roleId,createUserId,createTime,updateUserId,updateTime,del,enable from sysuser where id like concat('%',0,'%') LIMIT #{startLen},5000")
ArrayList<User> subList(@Param("startLen") int startLen);
}
修改完成后我們?cè)贉y(cè)試一波–>
耗時(shí)5秒左右,可以滿(mǎn)足業(yè)務(wù)需求
結(jié)束
目前基本的查詢(xún)已經(jīng)寫(xiě)完文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-615072.html
看到這個(gè)文章的還可以對(duì)以下方面進(jìn)行優(yōu)化:文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-615072.html
- 索引進(jìn)行優(yōu)化。
- 每個(gè)線(xiàn)程查詢(xún)多少條數(shù)據(jù)最為合適??
- 如果配置有線(xiàn)程池可以使用:總條數(shù)/線(xiàn)程數(shù)==每個(gè)線(xiàn)程需要查詢(xún)多少條數(shù)據(jù)。
- 進(jìn)行代碼優(yōu)化,優(yōu)化一些耗時(shí)的代碼。
到了這里,關(guān)于java查詢(xún)數(shù)據(jù)庫(kù)百萬(wàn)條數(shù)據(jù),優(yōu)化之:多線(xiàn)程+數(shù)據(jù)庫(kù)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!