图 1:本文封面,四根探针就是排查的四个怀疑方向。
先说明属性:这是一篇经典题解析,题目出自本地历史面试资料,与任何公司的当前招聘无关。「Redis 变慢怎么排查」是缓存运维题的常青款,面试官想听的不是某个偏方,而是一套有顺序、有依据的排查路径。
想象面试现场:「线上 Redis 响应突然变慢,你会从哪几个地方查起?」
第一步,先讲清排查的理论地基:单线程模型。
Redis 处理命令是单线程的——所有请求由一个事件循环顺序执行。这带来一个关键推论:任何一个慢操作都会堵住排在它后面的全部请求。所以「Redis 变慢」的排查本质,是回答一个问题:谁在独占主线程?带着这个问题,后面的每一步都有了方向。
第二步,入口动作:先把「命令慢」和「实例卡」分开。
一上来先做两件事。其一,用 SLOWLOG GET 看慢查询日志——Redis 官方文档把它列为低延迟运行的第一条检查项,日志里躺着执行超时的命令。如果慢日志里有明确的大命令(比如对一个百万级集合做排序、取交集),问题就锁定在「某条命令本身慢」,直接优化调用方式。其二,如果慢日志空空如也、但连 PING 都慢,说明是整个实例周期性地卡——这时祭出 INFO,去几个固定字段里找证据,进入下面四个怀疑方向。
方向一:fork 阻塞。
执行 RDB 后台保存或 AOF 重写时,Redis 主线程要 fork 出子进程。fork 本身要复制页表,实例内存越大越慢——官方文档给出的实测数据是物理机上每 GB 约十毫秒上下,虚拟化环境可能差一两个数量级。二十 GB 的实例 fork 一次,主线程就被冻结几百毫秒,期间所有请求排队。确认手段:INFO persistence 里的 latest_fork_usec 字段,记录了最近一次 fork 的耗时(微秒)。处置方向:控制单实例内存规模、避开高峰做持久化、关闭内核透明大页(THP,官方文档明确它会放大 fork 后的写时复制代价)。
方向二:bigkey。
一个几十万元素的集合,读它要一次性把几十 MB 数据搬上网卡,删它更糟——DEL 是同步回收内存的,主线程当场卡住。确认手段:redis-cli --bigkeys,它基于 SCAN 增量扫描整个键空间找出各类型最大的键,不影响在线服务。处置分两层:止血用 UNLINK 代替 DEL——官方文档说明 UNLINK 只把键从键空间摘除,真正的内存回收交给后台线程,不再阻塞;治本要拆,把大集合拆成多个小键,从数据结构上消灭 bigkey。
方向三:内存碎片。
键值反复增删改之后,操作系统实际占用的内存(RSS)会明显大于 Redis 数据本身的体积——分配器不会立刻把内存还给操作系统。确认手段:INFO memory 里的 mem_fragmentation_ratio,即 RSS 除以数据占用,明显大于 1(比如超过 1.5)说明碎片在吞内存,进而可能拖出交换分区,那是延迟的灾难。处置:开启动态碎片整理(activedefrag),或低峰期重启实例让内存归位。一个读数细节:官方文档提醒,内存刚从峰值回落时这个比率会失真偏高——RSS 记住的是峰值,要结合历史判断。
方向四:持久化开销。
AOF 的刷盘策略直接决定磁盘带来的延迟。官方文档把三档策略的取舍讲得很明白:always 每次写入都等 fsync 落盘再回复客户端,最安全但最慢;everysec 每秒刷一次、由后台线程执行,是官方认可的折中;no 完全交给操作系统,延迟最好但宕机丢失窗口最大。如果慢发生在磁盘繁忙时段,先看 appendfsync 配置和磁盘压力,在可接受的丢失窗口内调低刷盘频率。
图 2:排查决策路径与四个怀疑方向。
可能的追问与易错点。
「还有别的慢来源吗?」两个常被点名的:生产环境执行 KEYS 全库扫描,官方文档定性它只该用于调试,替代方案是 SCAN 族命令增量遍历;大量键设置在同一时刻过期,主动过期循环会集中清理造成抖动,给过期时间加个随机抖动就能抹平。易错点主要有三:一是不看 SLOWLOG 就凭直觉怀疑网络;二是拿 DEL 删大键止血,把偶发卡变成确定卡;三是把 mem_fragmentation_ratio 的一次高值当成定论,忽略峰值回落造成的失真。
回到面试现场,建议的答题节奏:先立「单线程、谁在独占主线程」的总纲,再给 SLOWLOG 加 INFO 的入口动作,然后沿 fork、bigkey、碎片、持久化四个方向各讲确认手段与处置。这套清单在 AI 服务里直接兑现:推理结果缓存一旦变慢,延迟会放大到整个调用链,四个方向就是缓存层运维的必修课。
你排查过最折腾的一次 Redis 变慢,最后的元凶是什么?欢迎分享。