redis变慢的元凶是linux的thp,因其使fork和cow开销暴增:启用后fork需复制2mb页表项,写操作触发cow时复制量达4kb页的512倍,导致incr/set延迟飙升至100ms+;必须同时设enabled和defrag为never、验证smaps无2048kb页,并重启redis。

Redis变慢的常见元凶之一就是Linux内核默认开启的Transparent Huge Pages(THP),它会让fork()和写时复制(COW)开销暴增,不是配置问题,而是内存管理机制与Redis工作方式的根本冲突。
为什么THP会让Redis fork卡住几百毫秒
Redis执行bgsave或bgrewriteaof时会调用fork()创建子进程。启用THP后,内核把原本4KB的小页合并成2MB大页;fork()虽不立即复制物理内存,但要复制页表项——而2MB页对应的页表项更少、更“重”。更关键的是,一旦父进程修改某块内存(比如执行incr),触发COW,就得把整个2MB页分裂、复制、映射,开销是4KB页的512倍。
- 现象:INFO commandstats里
incr、set等命令p50延迟突增至100ms+,slowlog频出 - 日志线索:
WARNING: You have Transparent Huge Pages (THP) support enabled in your kernel - 验证方法:查
/proc/<pid>/smaps</pid>,若出现大量MMUPageSize: 2048 kB,说明THP仍在生效
/sys/kernel/mm/transparent_hugepage/enabled设为madvise也不安全
很多人误以为madvise是折中方案,实际在Redis场景下几乎等同于always——因为Redis源码未在关键内存路径调用madvise(, MADV_HUGEPAGE),内核不会主动对它启用大页;但某些内核版本(如CentOS 7.6+的3.10.0-1127)在madvise模式下仍会fallback到always行为,khugepaged线程照常合并页。
-
always:强制所有匿名内存区域尝试合并,最危险 -
madvise:需显式标记才生效,Redis没标,但内核可能违规 fallback -
never:彻底禁用,连madvise请求都拒绝,唯一可靠选项
只改enabled不关defrag等于白干
临时执行echo never > /sys/kernel/mm/transparent_hugepage/enabled只是关了入口,后台线程khugepaged仍可通过defrag路径强行合并内存页。必须同步关闭:
echo never > /sys/kernel/mm/transparent_hugepage/enabledecho never > /sys/kernel/mm/transparent_hugepage/defrag- 检查两者输出都含
[never],且/proc/<pid>/smaps</pid>中无2048 kB项 - 最后重启
redis-server进程,否则旧进程仍运行在THP影响下
云环境和容器里THP更容易偷偷复活
AWS EC2、阿里云ECS或Kubernetes Pod中,即使你手动关了THP,也可能被系统级工具覆盖:
- AWS默认AMI常预装
tuned,其virtual-guestprofile会重开THP,需执行sudo tuned-adm off && systemctl disable tuned - 容器若挂载
/sys为只读,echo never > ...会失败;需在宿主机侧通过systemd服务或init脚本固化 - 某些CentOS 8.5+已弃用
/etc/rc.local,必须用disable-thp.service方式持久化
真正关掉THP不是改一个开关的事,得同时堵住enabled和defrag两个入口,并确认Redis进程重启后/proc/<pid>/smaps</pid>里全是4 kB页——否则延迟毛刺还会回来。











