MySQL写入变慢主因是Docker宿主机I/O调度器(cfq/bfq)延迟Redo Log fsync,需在宿主机切换调度器(NVMe设none、SATA SSD设noop)、调优innodb_log_file_size和log_buffer_size,并重启MySQL进程生效。

MySQL容器里写入变慢,先看Redo Log落盘是否被I/O调度拖住
直接结论:Docker默认使用cfq(或bfq)I/O调度器时,MySQL的innodb_flush_log_at_trx_commit=1会因频繁小IO被调度器合并/延迟,导致Redo Log fsync耗时飙升——不是MySQL慢,是内核拦在了落盘路上。
怎么确认是I/O调度器惹的祸
别猜,用实测数据说话:
- 在宿主机执行
cat /sys/block/*/queue/scheduler,看块设备(如sda、nvme0n1)当前调度器是不是cfq或bfq - 进MySQL跑
SHOW ENGINE INNODB STATUS\G,重点关注Log sequence number和Log flushed up to差值是否持续拉大(说明Redo没及时刷出) - 用
iostat -x 1观察await(平均IO等待时间)是否长期>5ms,%util不高但svctm高,大概率是调度器引入延迟
Docker容器无法直接改调度器?那就绕过它
容器本身不管理I/O调度,必须在宿主机层面调整——但别盲目切none(仅NVMe)或noop(旧SSD),得匹配硬件:
使用 Python 脚本下载 Docker 镜像并打包为 tar 文件。用户说“下载镜像”“拉取镜像”“pull xxx”“帮我下载 nginx 镜像”等时触发。支持 SOCKS5 代理直连(推荐)和国内镜像站加速两种模式。
- NVMe SSD:设为
none,命令:echo none > /sys/block/nvme0n1/queue/scheduler - SATA SSD:优先
noop,次选deadline;避免cfq/bfq - 机械盘(不推荐跑生产MySQL):保留
deadline,但需接受天然延迟 - 注意:Docker启动时加
--storage-driver overlay2并确保/var/lib/docker所在分区与MySQL数据目录不在同一物理设备上,否则日志和数据IO会互相干扰
Redo Log配置必须同步调优
换完调度器,如果还卡在fsync上,说明MySQL自身配置没跟上:
-
innodb_log_file_size太小(如默认48MB)会导致频繁checkpoint,放大I/O压力;建议设为1G~2G(根据写入量调整) -
innodb_log_buffer_size默认8MB,高并发事务下易频繁刷buffer;可提到64M减少log buffer flush次数 - 禁用
innodb_flush_method=O_DSYNC(它强制每次写都fsync,和innodb_flush_log_at_trx_commit=1叠加后雪上加霜),改用O_DIRECT - 验证:用
pt-stalk抓取fsync()系统调用耗时,若从20ms降到1ms以内,基本锁定I/O调度+配置协同生效
真正容易被忽略的是:调度器切换后必须重启MySQL进程(不是容器),因为InnoDB在启动时读取fsync行为并缓存底层设备能力。只改调度器不重启,等于白调。

















