Pgsql 大集合切割,多线程提高更新数据的速度

有一个优化的需求。要提高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>
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值