cgroup v2 不支持 write_iops 硬限速,因其 io.max 中的 max_iops_per_second 仅为调度提示,内核不统计完成 iops 也不拦截超限请求;推荐用 wbps 限制写入带宽(如 echo "8:0 0 52428800" > io.max),并结合数据库配置优化 io 行为。

在 Linux 中,cgroup v2 本身不直接支持 Write_IOPS 限速(即按每秒读写请求数限制),它只提供基于带宽(bytes/sec)的 io.max(v2)或 blkio.weight / blkio.throttle.*(v1)机制。IOPS(Input/Output Operations Per Second)是请求次数,而内核 I/O 调度层(如 mq-deadline、kyber)和块设备驱动并不向 cgroup 暴露“已完成 I/O 请求计数”的实时统计维度,因此无法原生做精确的 IOPS 上限控制。
为什么没有 Write_IOPS 这个参数?
根本原因在于:
- cgroup v2 的
io.max只接受major:minor max_bytes_per_second max_iops_per_second格式,但这里的max_iops_per_second是仅用于 bio 合并与调度提示的软上限,不是硬性拦截——内核不会拒绝超限的 I/O 请求,也不会统计实际完成 IOPS; - Linux 块层不维护每个 cgroup 的“完成 I/O 次数/秒”实时计数器,所以无法实现类似 CPU CFS 那样的周期性配额扣减;
- 实际 IOPS 受请求大小、队列深度、存储延迟、文件系统缓存等影响极大,固定 IOPS 限速在实践中意义有限且难以调优。
替代方案:用 io.max 限制写入带宽(推荐)
对数据库(如 PostgreSQL、MySQL)而言,限制写入带宽比限制 IOPS 更稳定、更可控,也更贴近真实负载压力。操作步骤如下(以 cgroup v2 为例):
- 确认系统启用 cgroup v2:
mount | grep cgroup应显示cgroup2; - 创建数据库专属 controller 目录:
sudo mkdir -p /sys/fs/cgroup/db-writer; - 将数据库进程加入该组(例如 PID=1234):
echo 1234 | sudo tee /sys/fs/cgroup/db-writer/cgroup.procs; - 设置写入带宽上限(例如限制为 50MB/s 到 /dev/sda):
echo "8:0 52428800 0" | sudo tee /sys/fs/cgroup/db-writer/io.max(其中8:0是sda的主次设备号,可用ls -l /dev/sda查看); - (可选)同时限制读带宽:
echo "8:0 0 52428800" | sudo tee /sys/fs/cgroup/db-writer/io.max。
间接逼近 IOPS 限速的方法
若业务强依赖 IOPS 控制(如规避 SSD 寿命或共享存储争抢),可通过以下方式近似实现:
-
结合平均 IO 大小估算:若数据库写请求平均为 4KB,则 10000 IOPS ≈ 40MB/s → 设置
io.max为8:0 41943040 0; -
使用内核模块或用户态代理:如
dm-thin+dm-queue-length、或通过fio+cgroup定期采样+动态调整io.max,但复杂度高、不推荐生产环境; -
应用层控制:在数据库配置中降低
checkpoint_timeout、max_wal_size(PostgreSQL)或innodb_io_capacity(MySQL),从源头减少突发写 I/O 密度。
验证与监控
检查是否生效:
- 查看当前限制:
cat /sys/fs/cgroup/db-writer/io.max; - 观察实时 IO 统计:
cat /sys/fs/cgroup/db-writer/io.stat(含 bytes 和 ios 字段,但 ios 是发出请求数,非完成数); - 用
iostat -x 1观察设备级r/s、w/s、avgrq-sz,反推实际 IOPS 表现; - 注意:cgroup v2 不提供 per-cgroup 的 IOPS 完成率直方图,需依赖设备级指标交叉分析。
不复杂但容易忽略:真正影响数据库 IO 性能的,往往是随机写放大、WAL 刷盘策略和 buffer pool 命中率,单纯限制 IOPS 可能掩盖更深的配置问题。建议优先优化数据库自身 IO 行为,再辅以 cgroup 带宽限速作为兜底手段。











