大事务卡在 binlog_cache_size 内存缓存中无法提交:默认值小(5.7为32kb,8.0为128kb),超限后触发串行磁盘写入,阻塞后续commit;可通过show variables和show status监控,调优需权衡内存与性能。

大事务卡在 binlog_cache_size 里出不去
不是所有大事务都会立刻刷盘,很多卡在内存缓存阶段就堵死了。MySQL 每个连接有自己的 binlog_cache_size,默认才 32KB(5.7)或 128KB(8.0)。一旦事务产生的 binlog event 超过这个值,就会触发磁盘临时文件写入(binlog_cache_use 和 binlog_cache_disk_use 计数器会跳变),而这个磁盘写入是串行的——后面所有事务的 COMMIT 都得排队等它。
- 查当前值:
SHOW VARIABLES LIKE 'binlog_cache_size'; - 看是否频繁落盘:
SHOW STATUS LIKE 'binlog_cache%';,如果binlog_cache_disk_use接近或等于binlog_cache_use,说明几乎每个事务都在刷磁盘 - 调大要谨慎:设太高(比如 > 512MB)可能引发单连接内存暴涨,尤其连接数多时;设太低(
- 推荐起步值:
SET GLOBAL binlog_cache_size = 4194304;(4MB),再观察binlog_cache_disk_use是否明显下降
max_binlog_cache_size 不是“越大越好”,而是“不能太小”
max_binlog_cache_size 是单个事务能用的最大 binlog 缓存上限,超了直接报错 ERROR 1197 (HY000): Multi-statement transaction required more than 'max_binlog_cache_size' bytes of storage。它不参与性能优化,只起熔断作用——防止一个事务吃光所有内存。
- 默认值通常是 4GB 或 16MB(版本差异大),但很多云厂商(如阿里云 RDS、腾讯云 TDSQL)会锁死该参数,无法修改
- 如果你自己管理 MySQL 实例,且确认业务中存在合法的大事务(如数据迁移),可适当调高,例如:
SET GLOBAL max_binlog_cache_size = 1073741824;(1GB) - 注意:该参数只对当前会话生效的事务起作用,且必须小于
max_allowed_packet,否则仍会失败 - 线上环境不建议无限制调高,更稳妥的做法是拆分事务,而不是靠调参硬扛
比调参更关键的是识别谁在生成巨量 binlog
盲目调大 binlog_cache_size 只是掩盖问题。真正该先做的,是定位哪些 SQL 在疯狂产 binlog。ROW 格式下,一次 UPDATE t SET c=1 WHERE id BETWEEN 1 AND 1000000 会生成百万级 event;而 STATEMENT 格式下就只有 1 条。
- 开启慢日志并捕获
COMMIT耗时异常的线程:SET GLOBAL long_query_time = 0.1;+ 开启log_slow_admin_statements - 查活跃事务和 binlog 估算量:
SELECT trx_id, trx_state, trx_started, trx_weight FROM information_schema.innodb_trx ORDER BY trx_weight DESC LIMIT 5;(trx_weight在 ROW 格式下近似于 binlog event 数) - 检查 binlog_format:
SHOW VARIABLES LIKE 'binlog_format';,如果是ROW且业务允许,切到STATEMENT或MIXED往往比调缓存立竿见影 - 别忽略隐式事务:autocommit=0 时,没显式
COMMIT的长连接,可能让 binlog cache 持续累积数小时
云数据库用户请优先看内核优化参数
自建库还能调缓存,但阿里云 RDS、腾讯云 TDSQL 等已把 binlog_cache_size 锁死。这时候再盯着这个参数就是白忙——它们早内置了更底层的优化机制。
- 阿里云 RDS MySQL 8.0(小版本 ≥ 20240731):启用
loose_binlog_cache_free_flush,配合loose_binlog_cache_free_flush_limit_size(默认 256MB) - 腾讯云 TDSQL MySQL 8.0(内核 ≥ 20230630):启用
txsql_non_blocking_binlog_threshold(最小阈值 134217728 字节,即 128MB) - 这两个参数本质都是:当事务 binlog 体积超过阈值,就把它导出到独立临时文件,绕过主 binlog 文件的串行锁
- 申请方式不是改配置文件,而是提交工单——控制台里找不到开关
真正容易被忽略的点是:事务 binlog 体积和你写的 SQL 行数未必线性相关,而和 binlog_format、字段长度、索引变更次数强相关。一张宽表更新 1 行,ROW 格式下可能产生几百 KB binlog。所以与其反复调 binlog_cache_size,不如先跑一遍 EXPLAIN FORMAT=TREE 看执行计划,再用 SHOW BINLOG EVENTS IN 'xxx' LIMIT 10 抽样看实际 event 大小。











