序章:一场令人冒汗的面试
"请问,如果你发现Redis突然变慢了,你会如何排查?"面试官推了推眼镜,眼神中透露着一丝玩味。
小王紧张地咽了口口水:“额…重启?”
面试官的嘴角抽搐了一下:“重启可以解决90%的问题,剩下的10%需要重启两次。不过作为一名专业的工程师,我们需要更加系统性的方法。今天我们就来聊聊Redis性能问题的排查之道。”
Redis真的变慢了吗?千万别冤枉了它
基准性能测试:给Redis做个体检
在给Redis判"死刑"之前,我们得先确认它是真的变慢了,还是我们的期望太高了。就像你不能因为法拉利在拥堵的环路上跑不过电动车就说法拉利不行一样。
如果观察到,这个实例的运行延迟是正常 Redis 基准性能的 2 倍以上,即可认为这个 Redis 实例确实变慢了。
# 测试Redis 60秒内的最大响应延迟
redis-cli --intrinsic-latency 60
# 查看一段时间内的延迟分布
redis-cli --latency-history -i 1
# 性能基准测试
redis-benchmark -h 127.0.0.1 -p 6379 -t set,lpush -n 100000 -c 50
小贴士:别让网络背锅
当用户连接到Redis通过TCP/IP连接或Unix域连接,千兆网络的典型延迟大概200us,而Unix域socket可能低到30us。记住要在Redis服务器本地测试,否则网络延迟会让你的Redis无辜躺枪。
Redis自身的"小毛病"
1. 慢查询命令:Redis的"慢性子"
Redis 使用了单线程的设计, 意味着单线程服务于所有的客户端请求,使用一种复用的技术。当一个请求执行得很慢,其他的客户端调用就必须等待这个请求执行完毕。
graph TD
A[客户端请求] --> B{命令类型}
B -->|O(1)| C[GET/SET/LPUSH<br/>秒级响应]
B -->|O(N)| D[SORT/LREM/SUNION<br/>可能很慢]
C --> E[正常响应]
D --> F[阻塞其他请求]
F --> G[整体性能下降]
排查方法:
# 查看慢日志
SLOWLOG GET 10
# 设置慢日志阈值(微秒)
CONFIG SET slowlog-log-slower-than 10000
解决方案:
- 避免使用O(N)以上复杂度的命令
- 执行O(N)命令时,保证N尽量小(推荐N ≤ 300)
- 将聚合操作放在客户端执行
2. 过期key集中删除:Redis的"大扫除"时间
Redis 内部维护了一个定时任务,默认每隔 100 毫秒(1秒10次)就会从全局的过期哈希表中随机取出 20 个 key,然后删除其中过期的 key,如果过期 key 的比例超过了 25%,则继续重复此过程,直到过期 key 的比例下降到 25% 以下,或者这次任务的执行耗时超过了 25 毫秒,才会退出循环。
现象特征:
- 变慢的时间点很有规律
- 某个整点或固定间隔出现延迟波动
解决方案:
- 设置随机的过期时间,避免集中过期
- 使用Redis 4.0+的lazy-free机制
# 开启lazy-free机制
lazyfree-lazy-expire yes
lazyfree-lazy-eviction yes
lazyfree-lazy-server-del yes
3. 操作bigkey:Redis的"负重前行"
当Redis处理大对象时,就像让一个人搬运一台钢琴,自然会比搬运一本书慢很多。
检测bigkey:
redis-cli --bigkeys
# 或者更详细的分析
redis-cli --bigkeys -i 0.1
解决方案:
- 将大key拆分为多个小key
- 使用UNLINK替代DEL删除大key(Redis 4.0+)
文件系统的影响:AOF的"拖后腿"
AOF持久化的双重性格
当 Redis 后台线程在执行 AOF 文件刷盘时,如果此时磁盘的 IO 负载很高,那这个后台线程在执行刷盘操作(fsync系统调用)时就会被阻塞住。此时的主线程依旧会接收写请求,紧接着,主线程又需要把数据写到文件内存中(write 系统调用),但此时的后台子线程由于磁盘负载过高,导致 fsync 发生阻塞,迟迟不能返回,那主线程在执行 write 系统调用时,也会被阻塞住。
解决方案:
- 使用高性能SSD
- 调整AOF刷盘策略
- 监控磁盘IO负载
# 修改AOF配置
appendfsync everysec # 推荐配置
操作系统的影响:真正的幕后黑手
1. Swap:Redis的噩梦
当内存中的数据被换到磁盘上后,Redis 再访问这些数据时,就需要从磁盘上读取,访问磁盘的速度要比访问内存慢几百倍。
检查Swap使用情况:
# 找到Redis进程ID
ps -aux | grep redis-server
# 查看Swap使用情况
cat /proc/$pid/smaps | egrep '^(Swap|Size)'
解决方案:
- 增加机器内存
- 禁用Swap或调整swappiness
- 分散Redis实例,减少单实例内存使用
2. 内存大页:看似美好的陷阱
主进程在拷贝内存数据时,这个阶段就涉及到新内存的申请,如果此时操作系统开启了内存大页,那么在此期间,客户端即便只修改 10B 的数据,Redis 在申请内存时也会以 2MB 为单位向操作系统申请,申请内存的耗时变长,进而导致每个写请求的延迟增加。
检查和禁用内存大页:
# 检查内存大页状态
cat /sys/kernel/mm/transparent_hugepage/enabled
# 禁用内存大页
echo never > /sys/kernel/mm/transparent_hugepage/enabled
3. Fork操作:Redis的"分身之术"
对于运行在一个linux/AMD64系统上的实例来说,内存会按照每页4KB的大小分页。所以一个空间大小为24GB的redis实例,需要的分页表大小为 24GB/4KB*8 = 48MB。当一个后台的save命令执行时,实例会启动新的线程去申请和拷贝48MB的内存空间。
优化方案:
- 控制Redis实例大小
- 在业务低峰期执行持久化操作
- 使用SSD减少fork时的内存申请延迟
实战排查清单
快速诊断命令组合
# 一键检查脚本
#!/bin/bash
echo "=== Redis性能诊断 ==="
echo "1. 基准延迟测试"
redis-cli --latency-history -i 1 -c 5
echo "2. 慢查询检查"
redis-cli SLOWLOG GET 10
echo "3. 内存使用情况"
redis-cli INFO memory
echo "4. 检查bigkey"
redis-cli --bigkeys --i 0.1
echo "5. 系统资源检查"
echo "CPU使用率:" && top -bn1 | grep "Cpu(s)"
echo "内存使用:" && free -h
echo "磁盘IO:" && iostat -x 1 1
尾声:面试官的满意笑容
小王经过一番学习后,再次面对面试官的提问,这次他胸有成竹地回答:“面试官,Redis变慢的排查需要系统性思维。首先要确认是否真的变慢了,通过基准性能测试来判断。然后从Redis自身、文件系统、操作系统三个层面来排查…”
面试官满意地点了点头:“很好,看来你已经掌握了Redis性能优化的精髓。记住,性能问题就像侦探小说,需要一步步收集线索,最终找到真凶。”
最后的面试问题总结(口语化回答):
"Redis变慢怎么排查?简单说就是三步走:
第一步,确认是不是真的慢了。用redis-cli --intrinsic-latency测个基准,看看是不是比正常情况慢了一倍以上。
第二步,查Redis自己的问题。看slowlog有没有慢查询,检查是不是有bigkey,还有是不是key集中过期了。这些都会让Redis主线程卡住。
第三步,看系统层面。检查磁盘IO、内存Swap、内存大页这些。特别是AOF刷盘和fork操作,都可能让Redis变慢。
总之就是先确认问题,再分层排查,从应用层到系统层,一步步缩小范围。别一上来就重启,那是运维的最后手段,不是第一选择!"
记住:Redis性能优化不是玄学,而是系统工程。每一个延迟背后都有其原因,找到它,解决它,让Redis重新飞起来!
293

被折叠的 条评论
为什么被折叠?



