Redis 实用场景全解析:不只是缓存

作者:杨帅      发布日期:2026.07.10

DATABASE · REDIS

Redis 实用场景全解析:不只是缓存

2026-07-10
R STR String HASH Hash LIST List ZSET Sorted Set SET Set GEO Geo HLL HyperLogLog STRM Stream ONE ENGINE · MANY DATA STRUCTURES

Redis 常被当作"缓存工具",但它的能力远不止于此。凭借丰富的数据结构和亚毫秒级响应速度,Redis 已经成为高并发系统里的多面手。本文梳理几个在实际项目中最常见、最有价值的 Redis 使用场景。

1. 缓存:降低数据库压力

这是 Redis 最经典的用法。把数据库中读多写少的数据(用户信息、商品详情、配置项等)放进 Redis,请求先查缓存,命中则直接返回,未命中再查数据库并回写缓存。

GET product:1001
        # 未命中 -> 查数据库 -> SET product:1001 "{...}" EX 3600
客户端 Redis 缓存 数据库 1. 查询 命中 → 直接返回 未命中 2. 回写缓存
请求先查缓存,命中直接返回;未命中查库后回写缓存

需要注意的三个经典问题:

  • 缓存穿透:查询不存在的数据,请求穿过缓存打到数据库。可以对空结果也做短期缓存,或用布隆过滤器提前拦截。
  • 缓存击穿:热点 key 过期瞬间大量请求涌入数据库。可以给热点数据加互斥锁重建,或设置逻辑过期不做物理过期。
  • 缓存雪崩:大批 key 同时过期导致数据库瞬间压力暴增。给过期时间加上随机值可以有效分散。

2. 会话存储(Session Store)

在分布式部署下,用户请求可能被负载均衡到任意一台服务器,本地 Session 无法共享。把 Session 统一存到 Redis,配合过期时间自动清理,就能让任意节点读取同一份会话状态,这也是 Session 集中化管理的标准做法。

SET session:abc123 "{userId:1, role:admin}" EX 1800

3. 分布式锁

多个服务实例同时操作同一资源时,需要一把"跨进程"的锁。Redis 的 SET key value NX EX seconds 可以原子地实现加锁,释放锁时用 Lua 脚本保证"判断持有者+删除"的原子性,避免误删别人的锁。

SET lock:order:1001 "worker-7" NX EX 10
lock:order:1001 Worker A SET NX ✓ 加锁成功 Worker B ✗ 加锁失败,等待 Worker C ✗ 加锁失败,等待
多个 Worker 争抢同一把锁,仅一个能加锁成功

生产环境更严谨的方案会用 Redlock 算法或者直接采用 Redisson 这类封装好的客户端,处理时钟漂移、锁续期(看门狗)等边界问题。

4. 排行榜与计分(Sorted Set)

游戏积分榜、热搜榜、销量排行这类"按分数排序"的场景,天生适合用 Sorted Set。一条 ZADD 写入分数,ZREVRANGE 就能拿到排名区间,ZRANK 能查任意成员的名次,复杂度都是对数级别。

ZADD leaderboard 9800 "player:alice"
        ZREVRANGE leaderboard 0 9 WITHSCORES   # 取前十名
        ZRANK leaderboard "player:bob"          # 查某玩家排名
ZREVRANGE leaderboard 0 4 WITHSCORES 🥇 1 player:alice 9800 🥈 2 player:bob 8600 🥉 3 player:carol 7450 4 player:dave 6100 5 player:erin 5200
ZREVRANGE 取出的排行榜结果

5. 计数器与限流

点赞数、浏览量、库存扣减这类高频自增场景,用 INCR/DECR 一条命令就能原子完成,避免了"读出来+加一+写回去"的竞态问题。

限流则常用滑动窗口或令牌桶算法配合 Redis 实现,比如结合 INCR + EXPIRE 做简单的固定窗口限流:

INCR rate:user:1001
        EXPIRE rate:user:1001 60   # 仅在第一次自增时设置
        # 若返回值超过阈值则拒绝请求
令牌桶 rate:user:1001 请求 ✓ 放行 请求 ✓ 放行 请求 ✗ 超限拒绝 正常处理请求
令牌桶耗尽后,超额请求被直接拒绝

更精细的场景可以用 Redis 自带的 CL.THROTTLE(RedisCell 模块)或 Lua 脚本实现漏桶/令牌桶算法。

6. 消息队列与发布订阅

Redis 的 List(LPUSH/BRPOP)可以当作简单的任务队列,Stream(XADD/XREADGROUP)则支持消费组、消息确认、历史回溯,适合做轻量级的消息中间件替代方案。Pub/Sub 则适合广播型场景,比如后台配置变更通知所有服务实例热更新。

XADD orders * orderId 1001 status "paid"
        XREADGROUP GROUP g1 consumer-1 COUNT 10 STREAMS orders >
生产者 id-1 id-2 id-3 new Stream: orders consumer-1 ✓ ACK consumer-2 ✓ ACK consumer-3 待消费
生产者写入 Stream,多个消费者各自消费并 ACK

需要强可靠、重投递、死信队列等企业级特性时,建议还是选择 Kafka、RabbitMQ 这类专职消息队列,Redis Stream 更适合中小规模场景。

7. 地理位置服务

Redis 的 Geo 数据结构(底层基于 Sorted Set + Geohash)可以存储经纬度,并直接查询"附近的人""附近的店"。

GEOADD stores 116.397 39.908 "store:1"
        GEOSEARCH stores FROMLONLAT 116.4 39.9 BYRADIUS 5 km ASC
GEOSEARCH ... BYRADIUS 5 km 我的位置 店铺 店铺 店铺 店铺(超范围)
以当前位置为圆心,检索半径内的门店

外卖、打车、社交类应用中"附近门店/附近的人"功能大多是这样实现的。

8. 去重与统计(HyperLogLog / Bitmap)

统计网站每日 UV、独立访客这种"只关心去重后数量、不关心具体是谁"的场景,用 HyperLogLog 只需要 12KB 左右的内存就能在标准误差 0.81% 内估算百万级基数,比直接用 Set 存储省内存得多。

PFADD uv:20260710 "user:1001" "user:1002"
        PFCOUNT uv:20260710

而用户签到、连续登录天数这类场景,用 Bitmap 一个 bit 代表一天,SETBIT/BITCOUNT 几行命令就能统计出月活跃天数。

HyperLogLog · 百万级 UV 只占 ~12KB 1,000,000+ 独立用户 user:1001, user:1002, ... ≈ 12 KB 误差 0.81% Bitmap · 用户 7 月签到记录(1 bit = 1 天) BITCOUNT sign:2026-07 → 10 天活跃
HyperLogLog 用极小内存估算基数;Bitmap 用比特位记录状态

9. 延迟任务与定时提醒

利用 Sorted Set 把"执行时间戳"作为分数,"任务内容"作为成员,一个后台线程定期 ZRANGEBYSCORE 取出到期任务并执行,就实现了一个简易的延迟队列,常用于订单超时取消、优惠券到期提醒等场景。

ZADD delay_tasks 1752134400 "order:1001:cancel"
        ZRANGEBYSCORE delay_tasks 0 <当前时间戳>
时间 → 现在 已执行 已执行 到期→执行 order:1001 取消 coupon:88 到期 reminder:99 ZRANGEBYSCORE delay_tasks 0 <now>
按到期时间戳排序,到点即触发对应任务

小结

从最基础的缓存加速,到分布式锁、排行榜、限流、消息队列、地理位置、去重统计、延迟任务,Redis 凭借 String、Hash、List、Set、Sorted Set、Bitmap、HyperLogLog、Stream、Geo 这些数据结构,几乎能覆盖高并发系统里大部分"快速读写+简单逻辑"的需求。选型时的关键不是"能不能用 Redis 实现",而是评估数据规模、持久化要求和可靠性诉求后,判断 Redis 是否是这个场景下最合适的工具。