跳到主要内容

Redis 性能优化

问题

Redis 有哪些常见的性能问题?如何发现和解决大 Key、热 Key?Pipeline 怎么用?

答案

一、性能问题排查

# 慢查询日志
CONFIG SET slowlog-log-slower-than 10000 # 超过 10ms 记录
CONFIG SET slowlog-max-len 128
SLOWLOG GET 10 # 查看最近 10 条慢查询

# 延迟监控
redis-cli --latency # 持续测量延迟
redis-cli --latency-history # 延迟历史

# 内存分析
redis-cli --bigkeys # 扫描大 Key
redis-cli --memkeys # 内存分析(Redis 7.0+)
MEMORY DOCTOR # 内存诊断建议

二、大 Key 问题

大 Key 的危害
  • 内存不均衡(Cluster 场景)
  • 操作阻塞主线程(尤其是 DEL)
  • 网络传输耗时长
  • 持久化影响(子进程 Copy-On-Write 时大页复制)

什么算大 Key?

类型大 Key 标准
Stringvalue > 10KB
Hash/List/Set/ZSet元素 > 5000 个 或 总大小 > 10MB

发现大 Key

# 方法 1:redis-cli 扫描
redis-cli --bigkeys

# 方法 2:SCAN + MEMORY USAGE
redis-cli SCAN 0 COUNT 100
redis-cli MEMORY USAGE mykey

# 方法 3:RDB 分析工具
rdb --command memory dump.rdb --bytes 10240

删除大 Key

# ❌ 直接 DEL 会阻塞
DEL huge_hash # 如果有 100 万元素,可能阻塞数秒

# ✅ UNLINK 异步删除(Redis 4.0+)
UNLINK huge_hash # 后台线程异步释放内存

# ✅ 分批删除
# Hash:HSCAN + HDEL
# Set:SSCAN + SREM
# ZSet:ZREMRANGEBYRANK 分批
# List:LTRIM 逐步缩小

三、热 Key 问题

高并发访问同一个 key,导致单个 Redis 节点 CPU 飙高。

发现热 Key

# Redis 4.0+ LFU 模式
redis-cli --hotkeys

# proxy 层统计
# 业务层统计

解决方案

方案说明
本地缓存在应用层加 L1 缓存(Caffeine/Guava)
读副本key 拆分为多个副本(key_1, key_2..random)
Redis Cluster 读从库READONLY 命令让从库提供读服务

四、Pipeline(管道)

Redis 客户端每次请求都有网络 RTT 开销。Pipeline 将多个命令一次性发送:

普通模式:                     Pipeline 模式:
cmd1 → server → response1 cmd1 ─┐
cmd2 → server → response2 cmd2 ─┤→ server → response1
cmd3 → server → response3 cmd3 ─┘ response2
response3

Pipeline 的注意事项:

  • 命令数量不宜过多(建议每批 100~1000 个)
  • Pipeline 不是原子操作(不同于 Lua 脚本和事务)
  • 注意内存:一批命令的响应会先缓存在客户端

五、Lua 脚本优化

# 原子执行多个操作
EVAL "
local stock = tonumber(redis.call('GET', KEYS[1]))
if stock > 0 then
redis.call('DECRBY', KEYS[1], 1)
return 1
end
return 0
" 1 stock:product:123
对比PipelineLua 脚本事务 (MULTI)
原子性
网络往返1 次1 次多次
条件逻辑
适用场景批量独立操作有条件逻辑的原子操作简单原子批量

六、其他优化建议

类别建议
key 设计业务前缀 + 冒号分隔,如 user:1:name
避免危险命令禁用 KEYS *、FLUSHALL,用 SCAN 替代
连接池使用连接池,避免频繁创建连接
序列化Protobuf/MessagePack 比 JSON 更小更快
过期策略所有缓存 key 都设过期时间
批量操作MGET/MSET 替代多次 GET/SET
Redis 6.0+开启多线程 IO(io-threads 4

常见面试问题

Q1: 为什么 KEYS * 命令是危险的?

答案
KEYS 命令会遍历所有 key(O(n)),在生产环境中可能有数百万个 key,会阻塞 Redis 主线程数秒。应该使用 SCAN 命令分批遍历(游标迭代,不阻塞)。

Q2: Redis 6.0 的多线程是怎么回事?

答案
Redis 6.0 引入了 多线程网络 IO

  • 命令执行仍然是 单线程(保持原子性和简单性)
  • 网络读写(read/write syscall)由多线程处理
  • 适合网络 IO 成为瓶颈的场景(如大 value 传输)

配置:

io-threads 4           # 线程数(建议 CPU 核数的一半)
io-threads-do-reads yes # 读也用多线程

Q3: Redis 事务是原子的吗?

答案
Redis 事务(MULTI/EXEC)保证的是 命令不会被其他客户端插入(串行执行),但 不支持回滚。如果事务中某条命令执行失败,其他命令仍然会执行。

真正的原子性需要用 Lua 脚本。


相关链接