Redis 最佳实践与应用模式
Redis 的强大不仅在于它的命令,而在于开发者如何利用它的特性解决分布式系统的常见挑战。
1. 缓存设计模式
缓存是 Redis 最核心的使用场景,以下是几种核心模式。
Cache Aside (旁路缓存)
这是最通用的模式,应用管理缓存和数据库。
- 读操作:应用先读缓存,没中则读数据库,并写入缓存。
- 写操作:应用先写数据库,成功后删除缓存。
[!NOTE]
为什么写操作是“删除”而不是“更新”缓存?
避免在高并发下由于写操作冲突导致缓存中存入旧数据,删除机制能利用缓存未命中的特性懒加载最新数据。
缓存雪崩、击穿与穿透
- 缓存雪崩:大量 Key 同时过期。
- 应对:在过期时间上加随机抖动,防止批量同时失效。
- 缓存击穿:热点 Key 在失效瞬间,大量请求打到数据库。
- 应对:设置逻辑过期(后台刷新),或使用互斥锁(如
SETNX)。
- 应对:设置逻辑过期(后台刷新),或使用互斥锁(如
- 缓存穿透:查询不存在的数据。
- 应对:缓存空结果,或使用 布隆过滤器 (Bloom Filter)。
2. 分布式锁 (Redlock)
在高并发场景下,确保多个实例只有一个能执行特定代码块。
# 获取锁 (设置 10 秒超时)
SET lock:order:123 "uuid_value" NX PX 10000
# 释放锁 (务必使用 Lua 脚本核对 UUID,避免误删他人锁)
EVAL "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end" 1 lock:order:123 "uuid_value"
3. 分布式速率限制 (Rate Limiting)
用于防止接口被恶意攻击或流量过载。
-- 高效 Lua 限流脚本
local key = KEYS[1]
local limit = tonumber(ARGV[1])
local window = tonumber(ARGV[2])
local current = redis.call('INCR', key)
if current == 1 then
redis.call('EXPIRE', key, window)
end
if current > limit then
return 0
else
return 1
end
4. 排行榜 (Leaderboards)
利用 Sorted Set (ZSet) 实现高效的排名统计。
# 提交分数
ZADD games:score 1500 user:1
ZADD games:score 2000 user:2
# 获取前 10 名
ZREVRANGE games:score 0 9 WITHSCORES
5. 消息通知与延迟任务
- Pub/Sub:实时消息广播。
- Stream:可靠的消息消费。
- 延迟任务:利用 ZSet 的分数存储执行时间戳,定时扫描过期任务。
高性能开发建议
- 使用 Pipeline:将多个独立命令打包发送,减少网络 RTT 开销。
- 避免 Big Key:单个 Key (如 List 或 Hash) 成员数量建议控制在 5000 以内,避免导出操作卡死。
- 合理使用多线程 I/O:在 Redis 6/7 中开启
io-threads提升网卡并发吞吐。 - 连接池管理:务必使用连接池,不频繁开关连接。
安全红线
- 绝对禁止
KEYS *:在生产环境中使用SCAN代替。 - 设置 Maxmemory:防止 Redis 无限制吞掉服务器系统内存导致 OOM。
- 绑定内网 IP:绝不允许为了方便让 Redis 暴露在公网端口。
这些模式是 Redis 在大型系统架构中保持稳定、高性能的基石。