针对Java SSM项目中5000个单号批量查询的优化,可通过多线程并行查询、线程同步控制、SQL优化、连接池调优及结果缓存等策略实现高效处理。具体方案如下:
1. 多线程并行查询- 核心思路:将5000个单号拆分为多个小集合(如100个集合,每个50个单号),利用多线程并行查询数据库,减少单线程串行查询的耗时。
- 实现步骤:
拆分集合:根据硬件资源(如CPU核心数)合理划分小集合数量,避免线程过多导致上下文切换开销。
线程池管理:使用ExecutorService创建固定大小线程池(如10个线程),避免频繁创建销毁线程的开销。
任务提交:为每个小集合提交查询任务,线程从线程池中获取并执行。
2. 线程同步控制(CountDownLatch)- 作用:确保所有线程完成查询后再汇总结果,避免主线程提前处理未完成的数据。
- 实现方式:
初始化CountDownLatch,计数器值为小集合数量(如100)。
每个线程查询完成后调用countDownLatch.countDown()递减计数器。
主线程通过countDownLatch.await()阻塞等待,直到计数器归零。
3. SQL查询优化- 索引优化:
为单号字段添加索引(如CREATE INDEX idx_tracking_number ON orders(tracking_number)),加速批量查询。
避免索引失效场景(如使用NOT NULL约束、避免在索引列上使用函数或计算)。
- 查询逻辑优化:
使用IN语句替代多次单条查询(如SELECT * FROM orders WHERE tracking_number IN ('A001','A002',...))。
限制返回字段(如仅查询SELECT tracking_number, status),减少数据传输量。
避免JOIN操作,若需关联数据可分两次查询后合并。
4. 数据库连接池调优- 参数配置:
最大连接数:根据并发线程数设置(如线程池大小为10,连接池最大连接数可设为15-20)。
最大等待时间:设置合理的等待超时时间(如5秒),避免线程长时间阻塞。
连接有效性检查:启用连接测试(如testWhileIdle=true),及时清理失效连接。
- 推荐连接池:HikariCP(高性能、低延迟)或Druid(监控功能强大)。
5. 查询结果缓存- 适用场景:查询结果相对稳定(如物流单号状态更新频率低)。
- 实现方式:
本地缓存:使用Guava Cache或Caffeine缓存查询结果,设置合理的过期时间(如10分钟)。
分布式缓存:若系统为集群部署,可使用Redis缓存结果,键为单号集合的哈希值。
缓存策略:采用“缓存穿透”防护(如缓存空结果)、“缓存雪崩”预防(如随机过期时间)。
示例代码(关键部分)// 1. 拆分单号集合(5000个单号拆分为100个集合,每个50个)List<List<String>> partitionedLists = Lists.partition(trackingNumbers, 50); // 2. 初始化CountDownLatch和线程池CountDownLatch countDownLatch = new CountDownLatch(partitionedLists.size());ExecutorService executorService = Executors.newFixedThreadPool(10);// 3. 提交查询任务for (List<String> subList : partitionedLists) { executorService.submit(() -> { try { // 执行批量查询(假设使用MyBatis) List<Order> orders = orderMapper.batchQueryByTrackingNumbers(subList); // 处理结果(如存入缓存或合并到全局结果集) processOrders(orders); } finally { countDownLatch.countDown(); // 线程完成时递减计数器 } });}// 4. 等待所有线程完成countDownLatch.await(5, TimeUnit.MINUTES); // 设置超时时间executorService.shutdown();补充建议- 批量查询大小:根据数据库性能调整每个小集合的单号数量(如50-200个),避免单次IN语句过长。
- 异常处理:捕获线程内异常并记录日志,避免因单个查询失败影响整体流程。
- 压力测试:使用JMeter或Arthas模拟高并发场景,验证优化效果并调整参数。
通过上述方案,可显著缩短5000个单号的查询时间,同时保证系统稳定性和可扩展性。