場景:定時(shí)任務(wù)查詢?nèi)頂?shù)據(jù)進(jìn)行處理推送時(shí)金踪,采用的策略是分頁每次查詢100條數(shù)據(jù)钠龙,進(jìn)行業(yè)務(wù)處理威创,直到所有數(shù)據(jù)都處理完成榕订。因?yàn)閿?shù)據(jù)量太大導(dǎo)致出現(xiàn):
SELECT * FROM table_name ORDER BY id ASC
![]()
這樣的分頁sql 性能是非常低的,因?yàn)閿?shù)據(jù)庫會(huì)從第一條數(shù)據(jù)遍歷到第3000000條蛹找,再進(jìn)行分頁姨伤。
public ReturnT<String> execute(String param) {
List<Person> persons;
for (int i = 0; ; i += 100) {
String limitSql = "limit " + i + ",100";
persons = personService.selectList(new QueryWrapper<Person>()
.lambda().orderByAsc(Person::getId).last(limitSql));
//=========================業(yè)務(wù)邏輯==========================================================
persons.forEach(e -> {
try {
rocketProducer.send(RocketMqConstant.PERSON_TOPIC, RocketMqConstant.PERSON_TAG, e);
} catch (Exception ex) {
log.error("異常, {}", ex);
}
});
//===========================================================================================
//終止循環(huán)
if (persons.size() < 100) break;
}
return ReturnT.SUCCESS;
}
從上面的代碼可以看出來,除了分頁sql的性能問題庸疾,分頁和業(yè)務(wù)也耦合在了一起乍楚。類似的代碼有13處,全是使用類似的形式編寫届慈,分頁每次查詢100條進(jìn)行業(yè)務(wù)處理的邏輯徒溪。
分頁與業(yè)務(wù)進(jìn)行拆分
- 分頁
public static <T> void selectByConPage(
Long start, Long size, BiFunction<Long, String, List<T>> function,
Function<List<T>, Long> functionMaxId, Consumer<List<T>> consumer) {
Long maxId = 0L;
for (; ;) {
// 拼接分頁條件.
String limitSql = "limit " + size;
// 執(zhí)行分頁查詢.
List<T> apply = function.apply(maxId, limitSql);
// 獲取最大id.
maxId = functionMaxId.apply(apply);
// 處理業(yè)務(wù)邏輯.
consumer.accept(apply);
// 最后一頁返回.
if (apply.size() < size) break;
}
}
因?yàn)槿际悄J(rèn)從0條開始,每頁100行拧篮,因此重載分頁方法
/**
* 默認(rèn)從0開始词渤,每頁100條.
*
* @param function
* @param functionMaxId
* @param consumer
* @param <T>
*/
public static <T> void selectByConPage(
BiFunction<Long, String, List<T>> function,
Function<List<T>, Long> functionMaxId, Consumer<List<T>> consumer) {
selectByConPage(0L, 100L, function, functionMaxId, consumer);
}
- 業(yè)務(wù)
private void business(List<Person> persons) {
persons.forEach(e -> {
try {
rocketProducer.send(RocketMqConstant.PERSON_TOPIC, RocketMqConstant.PERSON_TAG, e);
} catch (Exception ex) {
log.error("異常, {}", ex);
}
});
}
- 查詢
public List<Person> selectByCon(Long id, String limitSql) {
LambdaQueryWrapper<Person> wrapper = new QueryWrapper<Person>().lambda()
.gt(Person::getId, id)
.orderByAsc(Person::getId).last(limitSql);
return baseMapper.selectList(wrapper);
}
- 處理查詢結(jié)果,獲取最大id
public Long selectMaxId(List<Person> list) {
if (Optional.ofNullable(list).isPresent()) {
Long maxId = list.stream().map(Person::getId).max(Comparator.naturalOrder()).orElse(Long.MAX_VALUE);
return maxId;
}
return Long.MAX_VALUE;
}
- 改造完成之后
將查詢和業(yè)務(wù)的函數(shù)以參數(shù)的形式傳入串绩,達(dá)到分頁與業(yè)務(wù)處理邏輯的分離以及復(fù)用分頁的目的缺虐。
public ReturnT<String> execute(String param) {
PageAllMsgUtil.selectByConPage((id, limitSql) -> personService.selectByCon(id, limitSql),
apply -> personService.selectMaxId(apply), list -> business(list));
return ReturnT.SUCCESS;
}