[特殊字符] 引言:为什么 Redis 会突然“抽风”?

张开发
2026/6/1 19:01:34 15 分钟阅读
[特殊字符] 引言:为什么 Redis 会突然“抽风”?
导读你是否经历过这样的“至暗时刻” 业务高峰期原本毫秒级响应的 Redis 突然像 PPT 一样卡顿QPS 断崖式下跌紧接着上游服务大面积超时熔断。你满头大汗地登录服务器却发现 CPU 和内存都很“健康”百思不得其解。别慌你可能只是掉进了 Redis 的“单线程阻塞陷阱”。本文不讲废话直接通过7个真实生产事故场景带你复盘那些让运维工程师彻夜难眠的性能元凶并提供即拿即用的“外科手术式”排查方案。 引言为什么 Redis 会突然“抽风”Redis 官方号称单机 QPS 10万被誉为内存数据库的“闪电侠”。但在真实的生产环境中它却经常因为一个简单的操作而“瞬间窒息”。核心真相Redis 的核心命令处理是单线程的。这意味着任何一个耗时的操作都会像高速公路上的“幽灵堵车”阻塞后面所有的请求。让我们通过以下 7 个维度的“案发现场”逐一击破。 场景一慢查询命令 —— 那个被全表扫描拖垮的夜晚 事故现场 某电商大促前开发同学为了“确认一下数据”在生产环境执行了KEYS *。当时 Redis 实例中有 1.2 亿个 Key。结果Redis 阻塞了 30 秒导致库存服务不可用直接引发资损。1.1 核心原理Redis 处理命令的时间复杂度如果是 O(N)且 N 极大就会导致主线程长时间“霸占”CPU。1.2 高危命令清单 (请背诵并默写)命令族致命命令替代方案 (生与死的区别)全量扫描KEYS * (严禁生产使用)SCAN (游标迭代无阻塞)集合操作HGETALL, SMEMBERSHSCAN, SSCAN (分批获取)删除操作DEL (同步删除)UNLINK (Redis 4.0, 异步删除)1.3 避坑指南开启慢查询日志在redis.conf中设置 slowlog-log-slower-than 2000 # 记录超过 2ms 的命令 slowlog-max-len 1000 # 保留最近1000条日常巡检定期执行SLOWLOG GET 10发现慢查询立即优化。 场景二Big Key —— 一只大象踩死蚁群 事故现场 某社交 App 的“热榜”功能将全站 Top 10000 的帖子缓存为一个 Redis Hash。某天热榜更新程序读取这个 Hash 并序列化返回给前端。由于数据量过大20MB序列化耗时 500ms导致 Redis 在这半秒内对其他请求“视而不见”。2.1 什么是 Big KeyStringValue 10KB (或 1MB)集合元素个数 5000 (或 10万)2.2 如何发现使用 Redis 自带的扫描工具bash redis-cli --bigkeys -h host -p port -a password 提示该命令会遍历全库建议在低峰期执行。2.3 优化策略拆分将大 Hash 拆分为user:1000:part1,user:1000:part2。异步删除遇到无法避免的大 Key 删除必须使用UNLINK。 场景三AOF 刷盘 —— 磁盘 I/O 的“背刺” 事故现场某金融系统开启了 Redis AOF 持久化appendfsync always并挂载在普通的云硬盘上。当磁盘出现偶发的“毛刺”延迟升高时Redis 写入延迟瞬间飙升至秒级导致交易下单接口大面积超时。3.1 阻塞原理Redis AOF 的fsync策略决定了持久化的代价always数据最安全性能最差每次写都刷盘。everysec推荐每秒刷盘一次由后台线程执行。no性能最好数据可能丢失。3.2 修复方案硬件升级使用 SSD 或高性能云盘高 IOPS。配置优化CODE_BLOCK_3 场景四RDB 快照 ——fork()的“瞬间窒息” 事故现场某大数据平台的 Redis 实例内存高达 20GB。每天凌晨 2 点运维脚本自动执行BGSAVE备份。每次备份时fork()子进程耗时长达 3 秒导致线上支付请求在这 3 秒内全部挂起。4.1 核心痛点fork()操作需要复制父进程的内存页表。内存越大fork()耗时越长。在此期间Redis 主进程无法处理任何请求。4.2 数据诊断查看INFO stats中的latest_fork_usec字段如果该值 1000000 (1秒)说明fork非常慢必须优化。4.3 优化手段控制内存单实例内存建议控制在10GB 以内。关闭透明大页Transparent Huge Pagesecho never /sys/kernel/mm/transparent_hugepage/enabled(注开启大页会加重fork时的 COW 开销)错峰备份将备份任务安排在业务绝对低谷期。 场景五主从同步 —— 全量复制的“多米诺骨牌” 事故现场某次网络抖动导致 5 个 Redis 从节点同时与主节点断开连接。网络恢复后这 5 个节点同时向主节点发起全量同步SYNC。主节点连续fork()了 5 次瞬间被打满导致主节点服务不可用。5.1 阻塞链条从节点断开重连 - 触发全量复制。主节点执行BGSAVE-fork阻塞。主节点发送 RDB 文件 - 占用带宽。从节点加载 RDB - 从节点阻塞。5.2 解决方案无盘复制Diskless ReplicationRedis 2.8.18 支持主节点直接将 RDB 数据通过 Socket 发送给从节点不经过磁盘。repl-diskless-sync yes树状复制主 - 从A - 从B减少主节点的压力。增大复制积压缓冲区避免短时间断网就触发全量复制。⏰ 场景六过期键清理 —— “过期风暴” 事故现场某活动系统给 100 万个 Key 设置了相同的过期时间TTL3600。1 小时后这 100 万个 Key 同时失效。Redis 主线程为了清理这些 Key疯狂占用 CPU导致正常业务请求排队等待。6.1 避坑法则打散过期时间在设置 TTL 时增加一个随机数。# 错误做法 EXPIRE key 3600 # 正确做法 EXPIRE key 3600 random(0, 300) # 在 1小时 到 1小时5分 之间随机过期异步清理开启lazyfree-lazy-expire让后台线程处理过期键的内存释放。 场景七内存交换 (Swap) —— 比机械硬盘还慢 事故现场Redis 的性能监控显示延迟偶尔飙升但服务器内存监控显示还有空余。排查许久才发现Linux 开启了 Swap 分区。当 Redis 的部分内存页被交换到机械硬盘上时一次简单的读取操作需要从磁盘读取延迟从微秒级变成了毫秒级。7.1 如何判断查看INFO memory如果mem_fragmentation_ratio(内存碎片率)小于 1说明 Redis 使用了 Swap因为 RSS Used Memory。7.2 终极方案严禁 Redis 使用 Swap修改系统配置vm.swappiness 1(或 0)。硬性隔离确保 Redis 的maxmemory设置加上系统预留内存不超过物理内存的 70%。️ 总结Redis 防坑速查手册为了方便你记忆我将上述 7 大场景总结为“Redis 性能优化黄金法则”慢查询拒绝KEYS拥抱SCAN。大 KeyString 10KB集合拆分删除用UNLINK。持久化AOF 用everysec备份盘用 SSD。Fork单实例 10GB关闭透明大页。主从开启无盘复制避免多从节点直连主节点。过期TTL 加随机数防止集体自杀。内存严禁 Swap严禁 Swap严禁 Swap 写在最后Redis 的高性能是建立在“简单”和“可控”的前提下的。只要避开上述 7 个“深坑”它依然是你架构中最稳如泰山的组件。如果这篇文章帮你避免了一次线上事故或者让你在面试中脱颖而出请不要吝啬你的点赞和收藏

更多文章