跳到主要内容

Redis 应用场景

问题

Redis 有哪些典型应用场景?分别使用什么数据结构?

答案

一、场景总览

场景数据结构核心命令
缓存String / HashSET, GET, HSET, HGET
排行榜ZSetZADD, ZREVRANGE
计数器StringINCR, INCRBY
分布式锁StringSET NX EX
限流ZSet / StringZADD + ZRANGEBYSCORE / INCR + EXPIRE
延时队列ZSetZADD + ZRANGEBYSCORE
消息队列List / StreamLPUSH + BRPOP / XADD + XREAD
签到BitmapSETBIT, BITCOUNT
UV 统计HyperLogLogPFADD, PFCOUNT
附近的人GEOGEOADD, GEORADIUS
分布式 SessionString / HashSET, GET
全局 IDStringINCR

二、重点场景详解

1. 排行榜(ZSet)

# 添加/更新分数
ZADD ranking 100 "Alice"
ZADD ranking 95 "Bob"
ZINCRBY ranking 10 "Bob" # Bob 加 10 分

# Top 10(分数从高到低)
ZREVRANGE ranking 0 9 WITHSCORES

# 获取某人排名(0 开始)
ZREVRANK ranking "Alice"

# 获取某人分数
ZSCORE ranking "Alice"

2. 滑动窗口限流(ZSet)

# 限制:每分钟最多 100 次请求
local key = "rate:" .. user_id
local now = redis.call('TIME')[1] -- 当前时间戳
local window = 60 -- 60 秒窗口
local limit = 100

-- 移除窗口外的请求
redis.call('ZREMRANGEBYSCORE', key, 0, now - window)
-- 统计窗口内请求数
local count = redis.call('ZCARD', key)
if count < limit then
redis.call('ZADD', key, now, now .. math.random())
redis.call('EXPIRE', key, window)
return 1 -- 允许
else
return 0 -- 拒绝
end

3. 延时队列(ZSet)

# 生产者:将任务放入延时队列,score=执行时间戳
ZADD delay_queue 1704067200 '{"task":"send_email","to":"user@example.com"}'

# 消费者:轮询获取到期的任务
local tasks = redis.call('ZRANGEBYSCORE', 'delay_queue', 0, ARGV[1], 'LIMIT', 0, 10)
for _, task in ipairs(tasks) do
redis.call('ZREM', 'delay_queue', task)
-- 处理任务
end

4. 签到(Bitmap)

# 2024 年 1 月签到(key=sign:user:1:202401)
SETBIT sign:user:1:202401 0 1 # 1 号签到
SETBIT sign:user:1:202401 4 1 # 5 号签到

# 统计本月签到天数
BITCOUNT sign:user:1:202401 # 2

# 获取某天是否签到
GETBIT sign:user:1:202401 4 # 1(已签到)

# 内存:一个用户一个月只需 4 字节!

5. UV 统计(HyperLogLog)

# 记录访客
PFADD page:uv:20240101 "user1" "user2" "user3"
PFADD page:uv:20240101 "user1" # 重复不计

# 统计 UV
PFCOUNT page:uv:20240101 # 3

# 合并多天统计
PFMERGE page:uv:week page:uv:20240101 page:uv:20240102
HyperLogLog 特性
  • 误差率约 0.81%
  • 无论多少元素,只需 12KB 内存
  • 适合不需要精确计数的大量统计场景

6. 附近的人(GEO)

# 添加位置
GEOADD shops 116.40 39.90 "星巴克" 116.41 39.91 "麦当劳"

# 查找附近 3km 的店铺
GEORADIUS shops 116.405 39.905 3 km WITHCOORD WITHDIST ASC COUNT 10

# 两点距离
GEODIST shops "星巴克" "麦当劳" km

三、分布式 Session

传统 Session 存在 Web 服务器内存中,集群部署时需要集中存储:

客户端 → Nginx 负载均衡 → Web Server 1 ─┐
→ Web Server 2 ─┤→ Redis(Session 存储)
→ Web Server 3 ─┘

常见面试问题

Q1: Redis 做消息队列靠谱吗?

答案

方案可靠性说明
List (LPUSH + BRPOP)消费后即删除,不支持 ACK
Stream (XADD + XREADGROUP)支持消费组、ACK、持久化
专业 MQ (Kafka/RocketMQ)真正的消息队列

结论:轻量级、简单场景用 Redis Stream;对消息可靠性要求高的场景用专业 MQ。

Q2: 如何用 Redis 实现全局唯一 ID?

答案

# 方案 1:INCR(简单但单点依赖)
INCR global:order:id

# 方案 2:INCR + 时间戳 + 机器标识
# 每天一个 key,避免 key 增长过大
INCR order:id:20240101 # 返回序列号
# 最终 ID = 20240101 + 000001

相关链接