mysql在docker中运行慢主因是overlayfs写时复制与cgroup i/o控制失效叠加导致i/o延迟飙升,需启用cgroup v2+io.weight、改用xfs挂载并调优innodb_io_capacity等参数。

MySQL 在容器里跑得慢,八成不是 SQL 问题,而是 I/O 调度被 OverlayFS + 默认 cgroup IO 控制器双重压制了。
为什么容器里 MySQL 的 I/O 延迟比裸机高得多
根本原因不在 MySQL 配置本身,而在容器运行时对底层 I/O 的“透明拦截”:OverlayFS 的多层写时复制(Copy-on-Write)机制会让一次 INSERT 触发多次元数据更新和块拷贝;同时默认的 io.weight(cgroup v2)或 legacy blkio.weight(v1)对 MySQL 进程几乎不起作用——因为 mysqld 启动后 fork 出的大量后台线程(如 page cleaner、log writer)会脱离原始 cgroup 控制范围,I/O 请求实际由内核调度器按 best-effort 处理,导致随机读写争抢严重。
- 典型现象:
iostat -x 1显示%util不高但await持续 >20ms,r_await/w_await差异大 - 验证方式:在容器内执行
cat /proc/self/cgroup,再查对应 cgroup 的io.stat,常发现ios_p*计数远低于宿主机pidstat -d 1统计值 - 关键盲区:Docker 默认不挂载
io.max或io.weight,等于没开 I/O QoS,MySQL 和构建缓存、日志轮转等进程共用同一块 NVMe 的队列深度
必须改的三项容器启动参数
绕过默认调度失能,把 I/O 控制权拿回来:
- 强制使用 cgroup v2 +
io.weight:启动容器前确认宿主机已启用 cgroup v2(mount | grep cgroup应含cgroup2),然后用--cgroup-parent指定专用 cgroup 目录,并在其中写入io.weight(如echo "100 mysql" > io.weight) - 禁用 OverlayFS 元数据写放大:加
--storage-opt overlay2.override_kernel_check=true(仅限 Linux 5.10+),并确保/var/lib/docker/overlay2所在分区是 XFS(非 ext4),XFS 对 multi-layer rename 更友好 - 绕过容器网络栈干扰 I/O:MySQL 容器不要挂
--network=host,反而要显式用--network=none+host.docker.internal解析(避免 netfilter 规则意外触发磁盘日志写入)
MySQL 配置必须同步调整的三个点
容器 I/O 调度生效的前提,是 MySQL 自身不主动制造不可控 I/O:
-
innodb_io_capacity和innodb_io_capacity_max必须设为宿主机磁盘实测值(例如 NVMe 可设2000/4000),不能沿用物理机的200/400;否则 InnoDB 的后台刷脏页节奏完全脱节于容器实际带宽 -
innodb_log_file_size建议 ≥4GB(而非传统 256MB),减少日志文件切换频率;配合innodb_flush_log_at_trx_commit=2,让 log buffer 尽量走内存 flush,避开容器层额外 write() 系统调用 - 绝对禁用
sync_binlog=1—— 容器环境下它会让每个事务都触发一次fsync(),而 OverlayFS 的fsync()是全层同步,延迟爆炸;生产可用的是sync_binlog=100或直接关(sync_binlog=0)+ 半同步复制兜底
最易被忽略的挂载陷阱:/var/lib/mysql 不能 bind mount 到 ext4 分区
即使你用了 SSD,如果容器的 -v /data/mysql:/var/lib/mysql 挂载点落在 ext4 文件系统上,InnoDB 的 posix_fadvise() 预读提示会被静默丢弃,导致全表扫描时无法利用内核页缓存,每页都变成真实磁盘读。XFS 才真正支持 POSIX_FADV_DONTNEED 和 POSIX_FADV_NOREUSE。
- 检查命令:
df -T /data/mysql,输出不是xfs就得立刻迁移 - 迁移成本很低:停库 →
mkfs.xfs -f -n ftype=1 /dev/xxx→ 挂载 → 恢复数据;ftype=1 是为了支持 Docker overlay2 的 d_type - 顺手加挂载选项:
noatime,nodiratime,inode64,logbufs=8,logbsize=256k,尤其logbsize能降低 XFS 日志写放大
真正的瓶颈从来不在配置项列表里,而在容器运行时、文件系统、MySQL 存储引擎这三层边界模糊的交界处——那里没有文档,只有 iostat 和 perf record -e block:block_rq_issue 输出的真实字节流。











