Redis 最佳实践
系统:业务:实体:id:字段
示例:
user:info:1001
order:detail:20240101:9981
product:stock:sku123
优点:可读、可搜索、可分层。
- 推荐:20~40 字符以内
- 避免:超过 128 字符
否则网络传输成本会被放大。
危险的大 Key:
- Hash 有 10 万字段
- List 长度 100w+
- Set / ZSet 100w+
- String 大于 5MB
风险:
- 删除阻塞 Redis
- 内存涨幅不稳定
- 复制延迟骤增
解决方案:
- 拆分成多个 key(hash 分片)
- 使用 UNLINK 异步删除
- 使用 SCAN 代替 SMEMBERS / HGETALL
永远不要存永久 key!
推荐策略:
- 普通缓存:随机 TTL(防雪崩)
- 用户数据:30 分钟~2 小时
- 配置信息:5 分钟~1 小时
- 会话 token:30 分钟~12 小时
加随机偏移:
ttl = base + random(30~300)
解决:
- 布隆过滤器(推荐)
- 存空值 TTL=30s
解决:
- TTL 加随机
- 预热
- 多级缓存
解决:
setnx(lock)
query db
set cache
del lock
使用 SET NX EX 实现:
SET lock_key unique_value NX EX 30
释放锁使用 Lua 脚本(必须原子):
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
重要:
- value 必须唯一(UUID)
- 不能误删别人的锁
- 不要用 RedLock(复杂、也不绝对可靠)
INCR view:article:1001
两段式 GET -> 修改 -> SET 会并发错误。
推荐使用 Stream(生产最佳),不要用 List
为什么不用 List?
- 没有 ACK
- 没自动持久化
- 消费失败容易丢数据
Stream 优势:
- 消费组
- ACK
- 可追溯
- 可水平扩展
适用于对象:
user:info:1001 -> { id, name, age }
注意:
- 字段不要超过 5000
- 适合小对象,不适合大对象
- 避免 HGETALL,改用 HMGET
常用于排行榜、延迟队列
延迟队列示例:
ZADD delay_queue timestamp order_id
ZRANGEBYSCORE delay_queue 0 now
任务完成后删除:
ZREM delay_queue order_id
存储方式:
session:user:1001 -> token
token:xxxx -> user_info
注意:
- 设置 TTL
- 退出登录时删除 token
- 使用 Pipelining 批量操作提升性能
例如批量写 100 条 key:
pipe = redis.pipeline()
for i in range(100):
pipe.set(f"user:{i}", i)
pipe.execute()
使用场景:
- 批量写入
- 批量读取
不适合:复杂逻辑、依赖执行顺序的逻辑
在 redis.conf:
rename-command FLUSHALL ""
rename-command FLUSHDB ""
rename-command KEYS ""
rename-command CONFIG ""
rename-command SHUTDOWN ""
不要:
KEYS user:*
会阻塞整个实例!
要用:
SCAN 0 MATCH user:* COUNT 1000
maxmemory 4gb
maxmemory-policy allkeys-lru
理由:
- 防止 OOM
- Redis 做缓存时 LRU 效果最佳
最佳实践:启用混合持久化(默认)
RDB + AOF(选 everysec 选项)
appendfsync everysec
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
好处:
- 可靠性强
- 启动快
- 日志不会无限膨胀
- 开启复制 backlog(断点续传)
repl-backlog-size 64mb
- 主库禁用大 key
- 主从带宽足够
- 使用客户端读写分离(只读从库)
集群规模推荐:
3 主 + 3 从(6 节点)
最佳 key 设计:
user:{1001}:info
{} 内的内容用于 hash slot 固定。
禁止:
- KEYS、HGETALL、LRANGE 全量
- 大 key 操作(>5MB)
- 大列表一次性 LRANGE(需分段)
- Lua 脚本过长运行(超过 5 秒会阻塞)
必须:
- TTL + 随机
- 拆分大 key
- 使用 Pipeline
- 使用多级缓存(本地 cache + Redis)
- 使用 KEYS
- 大 key
- Lua 脚本卡死
- 复制积压
- 没 TTL
- key 太大
- 数据结构过大(List/Set)
- 主库压力大
- 全量同步频繁
- 网络瓶颈
- 客户端连接未复用
- 未使用连接池
- 所有缓存必须有 TTL
- 所有访问 Redis 的业务封装 SDK
- 不允许使用 KEYS / FLUSHALL / FLUSHDB
- 所有大 key 定期扫描
- 限流必须用 Lua
- 分布式锁必须用 SET NX EX + Lua
- 队列必须用 Stream
- 所有 Redis 命令响应时间 > 5ms 记录慢日志
🚀 Redis 生产环境最佳实践(完整指南)
🔥 目录
- 架构与部署
- 配置优化
- 性能优化
- 安全加固
- 内存管理与数据淘汰
- 备份与恢复
- 高可用方案推荐
- Python 生产级用法
- Kubernetes 部署最佳实践
- 常见坑与防踩坑指南
✅ 建议至少使用 主从 + 哨兵(Sentinel) 或 Redis Cluster
推荐结构:
| 场景 | 推荐架构 |
|---|---|
| 中小项目、高读写 | Master + Slave + Sentinel |
| 大规模、高并发、分布式缓存 | Redis Cluster(3 主 3 从) |
| 超大 KV 数据、水平扩展 | Redis Cluster |
| 延迟队列、多消费者 | Redis Stream |
maxmemory 4gb
maxmemory-policy allkeys-lru
rename-command FLUSHALL ""
rename-command FLUSHDB ""
rename-command CONFIG ""
appendonly yes
appendfsync everysec
save 900 1
save 300 10
save 60 10000
sudo swapoff -a
- 哈希、集合、多字段结构 使用 HASH
- 大列表改用 Redis Streams(更稳定)
大 key 问题=阻塞 Redis + CPU 爆炸
例如:
- 一个 List 保存 100 万行
- 一个 key 存储 10MB JSON
检查大 key:
redis-cli --bigkeys
高危命令:
| 命令 | 风险 |
|---|---|
| KEYS * | 阻塞服务器 |
| SMEMBERS | 集合超大时危险 |
| LRANGE 0 -1 | 列表过长时危险 |
| HGETALL | 字段太多危险 |
⚠️ 禁止 KEYS
使用 SCAN 替代:
for key in r.scan_iter("user:*"):
print(key)
requirepass your_password_here
bind 127.0.0.1
protected-mode yes
使用 Nginx、VPC、K8s ClusterIP 隔离。
最推荐策略:allkeys-lru
maxmemory-policy allkeys-lru
策略说明:
| 策略 | 推荐 |
|---|---|
| noeviction | ❌ 容易写失败 |
| volatile-lru | 不推荐 |
| allkeys-lru | ⭐ 正常缓存最佳选择 |
| allkeys-random | 备用 |
| volatile-ttl | 适合 TTL 类场景 |
cp appendonly.aof appendonly.aof.bak
cp dump.rdb dump.rdb.bak
Crontab:
0 */1 * * * cp /data/dump.rdb /backup/redis-$(date +\%Y\%m\%d\%H).rdb
master
├─ slave1
├─ slave2
sentinel x 3
- 支持自动分片 + 扩容
- 最常见配置:3 主 3 从
- 使用 local-path / PVC
- 每个节点固定调度
livenessProbe:
exec:
command: ["redis-cli","ping"]
initialDelaySeconds: 15
periodSeconds: 10
resources:
limits:
memory: "4Gi"
cpu: "1"
避免 OOMKill
| 问题 | 原因 | 解决 |
|---|---|---|
| Redis 突然卡死 | 使用 KEYS、大 key、慢命令 | 改用 SCAN、拆分大 key |
| 内存增加不下降 | 内存碎片 | 重启 Redis 或使用 jemalloc |
| 主从延迟 | 网络/大 key | 避免大 key,拆分结构 |
| CPU 飙高 | 大量 JSON、Lua 脚本 | 使用哈希或 RedisJSON |
| Python 卡住 | 未使用连接池 | 使用 ConnectionPool |