关键在于分层缓冲、批次控制与资源隔离:用有界arrayblockingqueue缓冲请求,scheduledthreadpoolexecutor定时批量消费,每批独占连接与事务,线程池参数需与数据库连接池(如hikaricp)对齐。

用线程池实现高并发下的批量数据入库,关键不在“多开线程”,而在于**分层缓冲 + 批次控制 + 资源隔离**。直接让上百个线程同时执行 insert 语句,极易打满数据库连接、触发锁竞争甚至连接超时。真正稳定高效的方案,是把“接收”“暂存”“组装”“写入”四个环节解耦。
一、用有界队列做第一道缓冲
避免请求直接冲击数据库。所有待入库数据先丢进一个容量可控的阻塞队列:
- 选 ArrayBlockingQueue(非 LinkedBlockingQueue),显式指定容量(如 2000),防止内存溢出;
- 生产者(如 API 接收端)调用
queue.offer(data),失败时可降级为本地缓存或异步重试; - 拒绝策略建议用 CallerRunsPolicy:当队列满时,由提交线程自己执行任务,自然限流,不丢数据也不抛异常。
二、用定时批量消费器代替“每来一条就插一条”
别用 Timer,改用 ScheduledThreadPoolExecutor(至少 corePoolSize=1)定期拉取批次:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 每次用带超时的循环
poll(10, MILLISECONDS)组装一批(比如目标 500 条,最多等 100ms); - 组装前加判空:
if (!batch.isEmpty()) { doBatchInsert(batch); },防空提交报错; - 单次拉取不强求“刚好满批”,宁可略少但及时,兼顾实时性与吞吐。
三、每个批次独占数据库连接和事务
这是避免连接争用和事务污染的核心:
- 每个消费任务内部新建 Connection(Spring 环境下用
@Transactional方法或TransactionTemplate); - 用 PreparedStatement.addBatch() + executeBatch(),不要循环 executeUpdate;
- 每批大小建议 200~500 条——太小调度开销大,太大易触发 MySQL 的
max_allowed_packet或事务锁时间过长。
四、线程池参数要跟数据库连接池对齐
线程池不是越大越好,必须匹配下游能力:
- 若 HikariCP 的
maximum-pool-size=20,线程池 corePoolSize 设 8~12 更稳妥; - 用 ForkJoinPool.commonPool() 或
newFixedThreadPool,别用 CachedThreadPool(可能无限创建线程); - keepAliveTime 可设为 60 秒,非核心线程空闲后及时回收。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










