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

容器里数据库慢,大概率不是 SQL 写得差,而是 I/O 被底层调度“卡住”了。OverlayFS 的写时复制、cgroup 对后台线程失控、磁盘队列争抢,三者叠加会让一次简单查询反复等待磁盘响应。
确认是不是 I/O 调度问题
别急着改 SQL 或加索引,先看现象是否匹配:
- iostat -x 1 显示 %util 不高(比如 20ms,r_await 和 w_await 差距明显
- 容器内执行 cat /proc/self/cgroup,再查对应 cgroup 的 io.stat,发现 ios_p* 计数远低于宿主机上 pidstat -d 1 统计的实际 I/O 次数
- 宿主机 top 看 mysqld 进程 CPU 占用低,但查询延迟高,且 innodb_buffer_pool_hit_rate 接近 99% —— 说明不是缓存不足,是磁盘响应拖慢了
绕过 OverlayFS 和 cgroup v1 的双重失效
Docker 默认的存储驱动和资源控制机制,在 MySQL 这类多线程 I/O 密集型服务上天然失能。必须手动接管:
- 启用 cgroup v2:宿主机运行 mount | grep cgroup,确保含 cgroup2;启动容器时用 --cgroup-parent 指定专用目录,并写入 io.weight(如 echo "100" > io.weight)
- 禁用 OverlayFS 元数据放大:加 --storage-opt overlay2.override_kernel_check=true(仅限 Linux 5.10+),且保证 /var/lib/docker/overlay2 所在分区格式为 XFS(非 ext4)
- 避免网络栈干扰 I/O:不要用 --network=host;改用 --network=none + host.docker.internal 解析,防止 netfilter 日志写入意外触发磁盘刷写
同步调优 MySQL 自身配置
I/O 控制生效的前提,是 MySQL 不主动制造不可控请求:
- 设 innodb_io_capacity 匹配 SSD 实际吞吐(如 NVMe 常设 2000–4000),并按比例设 innodb_io_capacity_max
- 调大 innodb_read_io_threads 和 innodb_write_io_threads(默认 4,SSD 环境可设为 8–12)
- 关闭 query_cache_type(已弃用),禁用 performance_schema 中非必要采集项,减少后台 I/O 干扰
验证与持续观察
优化后不能只看 QPS 上升,要盯住底层指标:
- 容器内 watch -n1 'cat /sys/fs/cgroup/io.pressure',压力值应稳定在 moderate 以下,avoided 长时间不涨说明调度生效
- 宿主机 iostat -x 1 观察 avgqu-sz(平均队列长度),理想值
- 对比 slow log 中相同 SQL 的 Rows_examined 和 Query_time,若后者下降而前者不变,基本锁定为 I/O 调度改善











