那天线上接口突然慢了,原本毫秒级的 Redis 响应变成了几百毫秒到几秒,部分服务直接超时。运维把流量临时削峰,用户投诉多了才发现问题和 Redis 挂钩,追查下来是主线程被占住了,导致大量请求排队。

事情要从最后的排队说起。影响最直观:请求堆积、响应延迟、连接数攀升,业务链路断裂。技术团队先把流量做了退避,避免更多请求涌入;同时把监控线拉满,看哪些指标飙升。慢查询日志、blocked_clients、CPU 使用率和磁盘 IO 都出现异常。接下来的排查,从外到内再反过来:先看外部资源(磁盘、网络、依赖服务),再看 Redis 本身的行为(大 key、耗时命令、持久化活动)。
把问题分成两类来观察比较有效——内部缘由和外部缘由。内部,指 Redis 自己做的工作占用了主线程;外部,指 Redis 在等待外部资源,导致不能及时响应。说白了,不管是哪一类,触发点都是同一个:Redis 的核心是单线程处理核心命令,主线程一旦被重负载占用,其他命令只能排队等候。
内部方面,几种常见触发器会把主线程掏空。列如某些命令本身就是阻塞型或复杂计算型:对大数据结构执行全量遍历的命令,像 KEYS *、SMEMBERS、ZRANGE 在巨量数据上,或者用 Lua 脚本做了大量迭代,都会把主线程绑很久。还有事务、pipeline 中塞入太多操作,同样会让响应变慢。另一个容易忽视的是“big key”问题:单个键里装了大量数据,操作这个键时耗时会瞬间飙升。遇到这些场景,表面看是 Redis 慢,实则是单个命令把主线程占满,让其他请求无时间片可用。
持久化也是常见的罪魁祸首。RDB 做快照需要 fork 进程,产生 copy-on-write,短时间内会带来大量内存写时开销和磁盘 IO;AOF 重写同样会引起磁盘压力。配置不当、磁盘性能不足或者频繁触发持久化,就有可能让主线程体验到“卡住”的感觉。要注意,持久化本身是为数据安全服务,但不合理的触发与不匹配的硬件会把它变成瓶颈。
外部因素看起来不在 Redis 内部,但影响更大。磁盘 IO 高、网络延迟、文件系统抖动会让持久化、AOF 写入和 repl 复制变慢;依赖的后端服务(例如慢查询引发回源请求)也会让 Redis 接受更多复杂请求。操作系统层面的参数没调好,列如 swap 被启用、vm.overcommit_memory 不对、ulimit 限制低,都可能在高并发下放大问题。简单说,Redis 单线程能跑多快,很大程度取决于外部环境是否配合。
定位问题的流程要有先有后,不能乱猜。现场处理时可以按这个思路走:先查看监控面板——被阻塞的客户端数、慢查询日志条目数、命令延时分布、CPU 与 IO 利用率、内存使用和连接数。接着抓取日志和慢日志,看看具体是哪类命令占用时间最长。然后看是否有并发的持久化事件(RDB bgsave、AOF rewrite)发生,或是后台复制阶段出现瓶颈。用 redis-cli info、MONITOR、SLOWLOG、latency 命令能拿到关键线索;配合系统工具如 iostat、vmstat、top 可以判断系统层面是否是 IO 或 CPU 的问题。
找到缘由后要有针对性处理,别盲目扩容。遇到大 key 或频繁的全量扫描命令,可以把数据拆表或改成分片存储,避免单个键承载太多。把 KEYS 换成 SCAN,SMEMBERS 换成分批读取或分页设计,慢函数移到后台异步执行。Lua 脚本要小步快跑,避免在脚本里做大量遍历或阻塞式调用。对于客户端发起的高并发写入,适当做限流或熔断,先保护 Redis 主线程。
针对持久化问题,有几招可试:调整 AOF 的 fsync 策略以降低 IO 压力(但要权衡数据安全),在低峰期触发持久化或把频繁重写的窗口移到业务空闲时段,确保磁盘是 SSD 并且有足够吞吐。还有一个常见做法是把只读压力分摊到从节点,让主节点主要承担写操作和同步,从节点来分担读取压力。要注意,BGSAVE 会 fork,内存不足或 copy-on-write 代价高时,fork 自身也会引发问题,这点要谨慎评估。
拓展维度是架构层面的调整。单实例承载不了的场景,就需要思考拆分成多个实例、做逻辑分片或上 Redis Cluster。集群可以把数据和请求分散,减少单点主线程被挂掉的风险。还可以在业务层面做缓存层次化设计:热数据放内存,冷数据落库,复杂计算结果放中间层,避免每次请求都打到 Redis 做重计算。
监控和预警要常年在位,不是出事才想起来。重大的监控指标包括命令延时分布、慢查询次数、blocked_clients、instantaneous_ops_per_sec、used_memory_peak、connected_clients、rdb_bgsave_in_progress、aof_rewrite_in_progress、cpu/io/wait 等。把这些指标设定阈值,异常时自动告警,提前把风险暴露出来。慢日志的阈值也要合理设置,记录能协助还原问题场景,查清是哪条业务命令拖了后腿。
现场紧急救火时,有些动作能临时缓和问题:把非必要的流量切掉、把从节点提升为只读流量路由、临时限制长耗时命令、对高并发客户端做速率限制、在极端情况下重启单个实例恢复服务(前提是评估风险并做好备份)。但这些都只是权宜之计,目的是赢得时间做深度排查和根本修复。
把这些排查和优化做一遍后,许多阻塞能被显著削弱。调整好持久化策略、拆分大 key、把重负载命令下沉到异步队列、上集群并建立完善监控,都是实操性强的路径。对技术团队来说,这类事挺常见,关键在于把“单线程被占用”这个核心问题看清楚,然后从外部和内部两个方向同时着手。说实在的,Redis 快的地方就是简单,但也恰好由于单线程,任何一块处理不好就会全盘影响,得细心维护。





