必须协同调整innodb_flush_log_at_trx_commit和sync_binlog:单独修改一个吞吐量几乎无提升且易致数据不一致;默认双1最安全但性能差;推荐组合为innodb_flush_log_at_trx_commit=2 + sync_binlog=0或1000,配合批量提交与组提交优化。

innodb_flush_log_at_trx_commit 和 sync_binlog 必须协同调整
单独改其中一个参数,吞吐量几乎没变化,还可能引入不一致风险。默认值 innodb_flush_log_at_trx_commit=1 + sync_binlog=1 是最安全组合,但每事务两次 fsync,SSD 上也扛不住 2000+ TPS。
实操建议:
-
innodb_flush_log_at_trx_commit=2:日志写入 OS buffer 即返回,崩溃最多丢 1 秒数据;写入吞吐通常翻倍以上 -
sync_binlog=0或sync_binlog=1000:关闭 binlog 强制刷盘,或改为每 1000 次事务刷一次;主从延迟会略增,但避免 binlog 与 redo 双重 I/O 压力 - 两项必须同时生效——否则 redo 刷得快、binlog 刷得慢,组提交(Group Commit)失效,性能提升归零
批量提交比单条提交省掉 90% 以上的 commit 开销
autocommit=1 下,每条 INSERT 都是独立事务,每次都要走 prepare → write log → fsync → commit 流程。网络往返 + 日志刷盘成了最大瓶颈,不是磁盘慢,是协议层太啰嗦。
实操建议:
- 显式关闭自动提交:
SET autocommit = 0,再用BEGIN/COMMIT包裹多条语句 - 每批控制在 100–1000 行之间:太少起不到合并效果,太多易触发锁升级或超时(
innodb_lock_wait_timeout默认 50 秒) - 避免在事务里做耗时操作(如 HTTP 请求、文件读写),否则锁持有时间不可控
别让 ON DUPLICATE KEY UPDATE 成为并发写入的隐形锁杀手
看起来是“存在则更新”,实际在高并发下唯一键冲突频繁时,InnoDB 会加 gap lock 或 next-key lock,导致大量线程卡在 update 状态,SHOW ENGINE INNODB STATUS 里能看到大量 waiting for table metadata lock 或 lock wait timeout exceeded。
实操建议:
- 确认业务是否真需要“upsert”语义;若只是高频插入,优先用纯
INSERT+ 忽略重复错误(ERROR 1062) - 若必须 upsert,考虑拆成两步:先
SELECT判断是否存在(加FOR UPDATE),再按需INSERT或UPDATE,但要严格按相同顺序访问表,防死锁 - 对计数类字段(如 pv、uv),改用 Redis 自增 + 定时异步落库,彻底绕过行锁
Redo Log 组提交不是默认就高效的
即使开了 innodb_flush_log_at_trx_commit=1,若 binlog_group_commit_sync_delay=0(默认),leader 线程几乎不等待,组大小常为 1~2,等于没启用组提交。
实操建议:
- 设
binlog_group_commit_sync_delay=10000(10ms),给 leader 收集更多事务的机会;配合binlog_group_commit_sync_no_delay_count=10,凑够 10 个就发,不等满延时 - 确保
sync_binlog=1已开启——否则 binlog 不刷盘,组提交逻辑不触发 - 避免长事务干扰:一个运行 30 秒的事务会阻塞后续所有事务进入 commit 阶段,组提交失效
真正卡住吞吐的,往往不是 SQL 写法,而是日志刷盘策略和事务边界设计。参数调得再激进,如果应用层还在循环执行单条 INSERT,或者事务里混着 sleep(1),所有优化都白搭。











