开放问答
JDBC 批量写入 batch size 加到多少吞吐开始不涨反跌?如何量化定位临界点
该问题的正文、回答与评论。
提问者 林若衡-251 · 状态 open
JDBC批量写入batch size性能调优MySQL
背景 我负责一条埋点落库链路:Kafka → 自研消费者 → JDBC 批量 INSERT 写 MySQL 8.0(InnoDB,binlog_format=ROW,sync_binlog=1,innodb_flush_log_at_trx_commit=1),单机 8C16G,rewriteBatchedStatements=true。目标是把单机吞吐从当前的 ~3 万行/s 提到 8 万行/s,P99 写延迟 ≤ 50ms。
现象 我把 batch size 从 500 一路加到 5000(每批一个事务),吞吐曲线大致是:500→2.1 万/s,1000→3.2 万/s,2000→4.5 万/s,3000→4.8 万/s,5000→4.1 万/s(反而下降)。P99 在 3000 时是 38ms,到 5000 时飙到 95ms。看起来存在一个临界点,过了之后越大批越慢。
已尝试的排查
- 确认 rewriteBatchedStatements=true 生效(抓了 SQL 日志,确实是 multi-row INSERT);
- 监控了 JVM:batch=5000 时 Young GC 频率从 2 次/分涨到 11 次/分,单次暂停 120~200ms(G1),怀疑是攒批内存对象(String 拼接 SQL + 参数数组)把 Eden 打满;
- 看了 MySQL 侧:batch=5000 时单个事务持锁时间从 ~8ms 涨到 ~45ms,InnoDB row locks 等待开始出现在其他并发事务上;
- 网络抓包:单帧从 ~120KB 涨到 ~800KB,没有明显丢包。
我的困惑 三个因素(JVM 内存压力 / InnoDB 单事务锁持有 / 网络帧大小)可能叠加,但我无法量化各自贡献了多少延迟。社区已有《JDBC 批量插入从 1200 行/s 提到 9 万行/s》这篇量化复盘(林墨白),但那篇侧重"为什么原来慢",没有正面回答"batch size 怎么定"。
期望的回答形态
- 有没有一个可操作的公式或二分法,能在给定吞吐目标 + P99 约束下反推 batch size 的最优区间?(比如"batch 大小 × 并发度 = 总吞吐预算"这种)
- 如何把 JVM GC 停顿、InnoDB 锁等待、网络 RTT 三个因素的贡献分别量化(而不是混在一起看 P99)?
- 如果 GC 是主因,有没有比"调 GC 参数"更本质的方案(比如流式拼 SQL 避免全量 String 持有)?
评论
暂无评论。