有一个优化的需求。要提高ip匹配接口的速度。这个接口主要逻辑有三步。
第一:查询需要操作的数据
第二:开始逻辑匹配
第三:将匹配好的数据更新回数据库
经过排查,发现第三步消耗的时间很多。
原来比较常规的处理逻辑代码如下
// list的大小有10w+
bikeIpService.updateIpAndAssignTime(list);
对应的mapper中的sql
<!--花费时间太长 -->
<update id="updateIpAndAssignTime2">
<foreach collection="list" item="item" separator=";">
update bike.public.bicycle_simip_info
set ip = #{item.ip} ,
assign_time = #{item.assignTime,jdbcType=TIMESTAMP},
assign_state = 1,
serial_number = #{item.serialNumber}
where id = #{item.id}
</foreach>
</update>
优化思路:
1.切分大集合,将大集合数据分为几个小集合,然后开启多线程去更新
2.优化更新的sql
具体实现代码
准备工作代码
public class SplitListUtils {
/**
* 切割list
* @param <T>
* @param list
* @param pageSize
* @return
*/
static public <T> List<List<T>> splitList(List<T> list, int pageSize) {
List<List<T>> listArray= ListUtils.partition(list, pageSize);
return listArray;
}
}
@Configuration
@EnableAsync // 启用异步任务
public class AsyncConfig {
// 声明一个线程池(并指定线程池的名字)
@Bean("async")
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 设置核心线程数
executor.setCorePoolSize(10);
// 设置最大线程数
executor.setMaxPoolSize(20);
// 缓冲队列500:用来缓冲执行任务的队列
executor.setQueueCapacity(500);
// 允许线程的空闲时间60秒:当超过了核心线程出之外的线程在空闲时间到达之后会被销毁
executor.setKeepAliveSeconds(60);
// 线程池名的前缀:设置好了之后可以方便我们定位处理任务所在的线程池
executor.setThreadNamePrefix("async-");
// 设置拒绝策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
@Component
@Slf4j
public class AsyncService {
@Async("async")
public void updateIpAndAssignTime(List<BikeSimIpInfo> list){
String threadName = Thread.currentThread().getName();
log.info("线程开始调用:{}",threadName);
bikeSimIpMapper.updateIpAndAssignTime(list);
}
}
//1.切分大集合,将大集合数据分为几个小集合
List<List<BikeSimIpInfo>> lists = SplitListUtils.splitList(bikeSimIpInfos, 1000);
//2.多线程更新
for (List<BikeSimIpInfo> list : lists) {
asyncService.updateIpAndAssignTime(list);
}
//3.sql优化 【记住一定要加上 else 的情况,如果不加的话,当when条件不符合时,会导致你要修改的字段被置空,是很危险的。】
<update id="updateBatch">
UPDATE jo_table SET
column1 =
CASE id
<foreach collection="list" item="item">
WHEN #{item.id} THEN #{item.column1}
</foreach>
ELSE column1
END,
column2 =
CASE id
<foreach collection="list" item="obj">
WHEN #{item.id} THEN #{item.column2}
</foreach>
ELSE column2
END
WHERE id IN
<foreach collection="list" item="item" separator="," open="(" close=")">
#{item.id}
</foreach>
</update>