跳到主要内容

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 在大型系统架构中保持稳定、高性能的基石。