必须先验证系统支持cgroup v2 io控制器:运行mount | grep cgroup2确认挂载cgroup2,再执行cat /proc/cgroups | awk '$1=="io" && $4==1'检查io控制器是否启用;若无输出,需在GRUB中添加cgroup_no_v1=all并重启。

确认系统是否支持 cgroup v2 io 控制器
不支持就白忙——很多老系统(如 CentOS 7 默认内核 3.10)只启用了 cgroup v1 的 blkio,而 io.max 是 v2 才有的硬限机制。先跑两行命令验证:
-
mount | grep cgroup2必须输出类似cgroup2 on /sys/fs/cgroup type cgroup2 -
cat /proc/cgroups | awk '$1=="io" && $4==1'有输出才说明io控制器已启用
若无输出,需在 /etc/default/grub 的 GRUB_CMDLINE_LINUX 中加 cgroup_no_v1=all,再 sudo update-grub && reboot。Ubuntu 22.04+/CentOS 8+ 一般默认 OK。
查准目标磁盘设备号并设置 wbps/rbps 限速
写错设备号是限速失效最常见原因。不要用 /dev/sda 这种路径,必须用主次号(如 8:0):
-
ls -l /dev/sda→ 看到brw-rw---- 1 root disk 8, 0,即设备号是8:0 -
ls -l /dev/nvme0n1→ 常见为259:0,不是259:1
新建限速组并写入带宽上限(例如限制 MySQL 写入为 20 MB/s):
sudo mkdir -p /sys/fs/cgroup/mysql-io echo "8:0 wbps=20971520" | sudo tee /sys/fs/cgroup/mysql-io/io.max
注意:wbps 单位是字节/秒,20 MB/s = 20 × 1024 × 1024 = 20971520;若还要限读,追加 rbps=10485760 即可。
把服务进程 PID 迁入 cgroup 并验证归属
不能只写配置,必须让进程真正运行在该 cgroup 下。两种方式:
- 对已运行的服务:先
pgrep -f "mysqld"拿到 PID,再echo $PID | sudo tee /sys/fs/cgroup/mysql-io/cgroup.procs - 对 systemd 服务:改
systemctl edit mysqld,加[Service]段:IOWeight=100和IOWriteBandwidthMax=/dev/sda:20M
验证是否成功迁移:
cat /proc/$PID/cgroup | grep io
输出中必须含 /mysql-io。若失败,检查该 PID 是否已在其他 io cgroup 中(cat /sys/fs/cgroup/*/cgroup.procs | grep $PID),清空原组再试。
实测限速是否真实生效,而非仅“配置成功”
配置写进文件 ≠ 效果落地。Buffered I/O(普通写)会被 page cache 缓冲,看起来没限住——必须用 Direct I/O 测试:
- 看实时统计:
cat /sys/fs/cgroup/mysql-io/io.stat | grep wbps,数值应稳定在设定值附近 - 用
dd实测:dd if=/dev/zero of=/var/lib/mysql/test bs=1M count=1000 oflag=direct conv=fdatasync - 同时开
iostat -x 1 /dev/sda,观察wkB/s是否卡在 ~20480(即 20 MB/s)
若仍超限,优先排查:设备号填错、进程未真在该 cgroup、测试时没加 oflag=direct、或内核未编译 CONFIG_BLK_CGROUP_IO_CTRL(zcat /proc/config.gz | grep IO_CTRL 查)。


















