innodb_flush_method=o_direct单独启用无法缓解swap压力,因其仅绕过page cache写.ibd文件,而redo log仍走os cache,导致buffer pool与log buffer争抢内存触发page reclaim;必须配合swappiness=1、memlock锁定内存及验证三项硬指标(变量值、capbnd权限、vmstat零si/so)才有效。

innodb_flush_method=O_DIRECT 能缓解 swap 压力,但单独配它基本没用——它只绕过 page cache 写数据文件(.ibd),而 redo log 默认仍走 OS cache,buffer pool 和 log buffer 在内存里抢页,反而加剧 reclaim,推高 swap 倾向。
为什么 innodb_flush_method=O_DIRECT 单独启用会失效
常见现象是:改了配置、重启 MySQL、SHOW VARIABLES LIKE 'innodb_flush_method' 确实返回 O_DIRECT,但 vmstat 1 里 si/so 还在跳,free -h 的 SwapUsed 持续涨。
根本原因是:O_DIRECT 不影响 redo log 的写入路径。InnoDB 的 log buffer 刷盘默认仍经由 OS page cache,尤其在写密集场景下,log buffer 和 buffer pool 共同触发内存压力,内核被迫换出匿名页(即 buffer pool 中的页),导致 swap 毛刺。
- 必须确认 my.cnf 中
[mysqld]段明确写了innodb_flush_method=O_DIRECT,不能拼错或落在错误 section - 不要和
innodb_flush_method=O_DSYNC混用——后者不绕过 cache,性能更差,且无法抑制 swap - 启用后,
innodb_buffer_pool_size实际占用物理内存更“实”,若原来设到 80%,现在得压到 ≤70%,否则 mysqld 启动直接失败 - 验证是否生效:启动后执行
SHOW VARIABLES LIKE 'innodb_flush_method',输出必须是O_DIRECT,不是空或async_unbuffered
innodb_flush_method=O_DIRECT 必须搭配 swappiness=1 和 memlock
只开 O_DIRECT 是半截子工程。它解决的是“写路径争抢”,但挡不住内核在内存紧张时把 mysqld 进程整个换出——哪怕只换出一页,InnoDB 的 LRU 链表就错乱,查询延迟飙升。
-
swappiness=1是压制换页冲动的临时阀门:执行sudo sysctl vm.swappiness=1立即生效,再写入/etc/sysctl.conf固化;值设为 0 并不能阻止 OOM 前换出,1 是生产环境更稳妥的选择 -
memlock才是根治手段:在/etc/security/limits.conf加两行:mysql hard memlock unlimited和mysql soft memlock unlimited;my.cnf 中加innodb_use_sys_malloc=0(若用 jemalloc);必须用 root 启动 mysqld,否则不生效 - NUMA 架构下(如多路 Xeon),
memlock可能白开:需额外配innodb_numa_interleave=ON,或用numactl --interleave=all启动 mysqld
验证 O_DIRECT 是否真起作用的三个硬指标
别信配置文件写了就完事。真正起效必须同时满足:
-
SHOW VARIABLES LIKE 'innodb_flush_method'返回O_DIRECT - 启动后检查
/proc/$(pgrep mysqld)/status | grep -i "cap",输出中CapBnd必须含cap_ipc_lock(说明memlock权限已获取) - 持续运行 5 分钟后,
vmstat 1中si和so长期为 0,且free -h的SwapUsed不再上涨
只要其中任一条件不满足,swap 卡顿随时可能复发——尤其是 NUMA + memlock 未配 innodb_numa_interleave 的组合,最容易被忽略。











