mysql 5.7 的 log_sys->mutex 是全局单点锁,保护日志缓冲区分配、写入与刷盘协调,所有事务提交均需串行争抢,高并发下成为性能瓶颈;调大 innodb_log_file_size 无效,应增大 innodb_log_buffer_size 并考虑设 innodb_flush_log_at_trx_commit=2 以缓解争用。

MySQL 5.7 的并发写性能卡在 log_sys 锁上,不是配置没调好,而是 InnoDB 日志子系统在高并发 INSERT/UPDATE 场景下,log_sys->mutex 成为单点争用热点——尤其当 innodb_flush_log_at_trx_commit=1(默认)且事务提交频率高时,所有线程必须串行排队获取该 mutex 才能写 redo log。
为什么 log_sys mutex 会成为瓶颈
这个锁保护的是整个日志缓冲区(log buffer)的分配、写入和刷盘协调逻辑。它不区分事务,也不按页或段拆分,所有涉及 log 写入的操作都得先抢这把全局锁:
-
INSERT、UPDATE、DELETE只要修改数据,就必须记录 redo,就得进 log_sys mutex 临界区 - 即使事务只改一行,也要走完整日志路径:log buffer 分配 → 拷贝日志内容 → 标记 LSN → 刷盘协调(如果 flush_mode=1)
- 当 QPS 超过 2000–3000(视 CPU 核数和磁盘延迟而定),
SHOW ENGINE INNODB STATUS中常出现大量log_write_up_to或log_flush_up_to等待,OS WAIT ARRAY INFO显示大量线程在log_sys->mutex上自旋或挂起
innodb_flush_log_at_trx_commit=1 是触发器,不是罪魁
设成 1 确保崩溃安全,但它让每次 COMMIT 都强制 fsync redo,放大了 log_sys 锁的串行效应。关键不是“要不要持久化”,而是“能不能批量交由后台线程处理”:
-
innodb_flush_log_at_trx_commit=2:log buffer 每秒刷一次磁盘,但事务仍写入 buffer —— 这能大幅缓解 mutex 争用,代价是崩溃最多丢 1 秒数据 -
innodb_flush_log_at_trx_commit=0:更激进,完全依赖后台线程刷盘,风险更高,仅限非核心业务 - 真正要注意的是:
innodb_log_buffer_size是否太小(默认 16MB)。buffer 小 → 更频繁触发 flush → 更多 mutex 抢占。大事务或高并发下建议设为 64M 或 128M
别指望单纯调大 innodb_log_file_size 能解决问题
innodb_log_file_size 控制的是 redo log 文件大小,影响的是 checkpoint 频率和 recovery 时间,**不缓解 log_sys mutex 争用**。增大它甚至可能让单次刷盘更重,加重 I/O 延迟对锁持有时间的拖累:
- log file 大 → checkpoint 间隔拉长 → 每次刷脏页量更大 → 后台 io_thread 压力上升 → 反向影响 log write 性能
- 真正影响
log_sys的是 log buffer 分配与刷盘调度逻辑,跟文件尺寸无关 - 验证是否真卡在这里:查
SHOW STATUS LIKE 'Innodb_log_waits',持续 > 0 就说明 log buffer 频繁满,buffer size 或 flush 频率已到极限
MySQL 5.7 没有真正的 log mutex 拆分机制
8.0 引入了 log_writer 和 log_flusher 线程分离,把日志写入和刷盘解耦,显著降低 mutex 持有时间。但 5.7 全靠单个 log_sys->mutex 统一管控,所有路径共用同一把锁:
- 没有 per-buffer-segment 锁,也没有 writer/flusher 分离
- 哪怕你开了
innodb_parallel_read_threads或调大innodb_thread_concurrency,对 log_sys 锁毫无帮助 - 唯一绕过方式是减少单位时间内的 COMMIT 次数:合并小事务、用批量 INSERT 替代循环单行、启用
autocommit=0+ 手动控制 commit 时机
log_sys 锁的问题藏得深——它不报错、不超时、不显式阻塞,只表现为写入延迟突增、TPS 上不去、CPU user% 高但 iowait 不高。最容易被忽略的是:你以为在优化 SQL 或索引,其实瓶颈早就在日志子系统里卡死了。











