1. 课程导入与业务场景拆解
在开始写第一行 Redis 命令之前,我们需要先弄清楚一件事:这门课要解决的到底是什么问题。很多同学一听到"排行榜"就想到 ZSET,一听到"计数器"就想到 INCR,但真正落到业务里,往往会在"用哪个结构""数据怎么存""什么时候更新""榜单怎么分页""计数会不会丢"这些细节上卡住。本章的目标不是给出答案,而是把问题拆清楚——先看清业务长什么样,再决定 Redis 在其中扮演什么角色。
一、实时排行榜与计数业务到底"实时"在哪里
传统做法是把积分、销量、点赞数写进关系型数据库,需要榜单时用 ORDER BY score DESC LIMIT 100 查一次。这套方案在数据量小、更新频率低时完全够用,但一旦进入"实时"场景就会暴露三个硬伤:
- 写入压力:一次点赞就是一次
UPDATE,热门内容每秒可能被更新上千次,行锁和写放大让数据库很快成为瓶颈。 - 查询延迟:每次刷新榜单都要对全表或大范围数据排序,数据量到百万级后,即使有索引,深分页和排序开销也不可接受。
- 时效性:用户刚投完票,榜单要"立刻"变化。数据库主从复制、缓存过期策略都会引入秒级甚至更长的延迟。
所谓"实时",在工程上通常指写入后百毫秒级内可被榜单查询感知。Redis 把数据放在内存中,单机读写可达十万级 QPS,天然适合承担"高频写入 + 低延迟读取"的中间层角色。它不是要取代数据库,而是把"热数据"和"高频操作"从数据库里剥离出来。
二、四类典型场景与它们的差异
同样是排行榜,不同业务的约束差别很大,直接决定了数据结构选型和更新策略。下面这张图把四类典型场景的核心特征、数据形态和 Redis 角色放在一起对照,便于建立整体认知。
从图中可以读出几个关键判断:游戏积分榜的分数是单调递增的,写入集中在对局结束,对延迟容忍度相对高;电商销量榜需要按天/周重置,key 的命名和过期策略比数据结构本身更重要;社交热度榜的分数不是简单累加,而是多行为加权并随时间衰减,需要额外的重算逻辑;实时计数器则完全不需要排序,用最简单的 String 就能解决。把场景特征先对齐,后面的选型才不会走偏。
三、Redis 在架构中的角色定位
需要特别强调的是:Redis 在这类业务中通常不是唯一数据源。以电商销量榜为例,订单数据必须落库保证不丢,Redis 里的销量榜是"派生数据"——它可以从数据库重算出来。这种定位带来两个重要推论:
- 允许最终一致:Redis 写入失败或实例重启导致榜单短暂不准,可以通过重算修复,不必像订单表那样要求强一致。
- 需要设计回源与重建路径:当缓存丢失或数据异常时,要能从数据库重新灌入榜单。这条路径必须在设计阶段就想清楚,而不是等线上出问题再补。
反过来,如果是"点赞数"这种允许少量误差、但要求极高并发的计数,Redis 可以直接作为权威计数源,再异步落库。所以"Redis 是缓存还是主存储"没有统一答案,取决于业务对一致性和持久性的要求。
四、动手前先明确四个问题
在进入具体命令和代码之前,建议对每个排行榜/计数需求先回答下面四个问题。它们会直接决定后续的 key 设计、结构选型和更新策略:
| 问题 | 为什么重要 | 示例回答 |
|---|---|---|
| 排序维度是什么? | 决定用 ZSET 还是 String | 按积分排序 → ZSET;只统计总数 → String |
| 更新频率与峰值? | 决定是否需要批量/异步写入 | 每秒 5000 次点赞 → 考虑合并写入 |
| 榜单周期? | 决定 key 命名与过期策略 | 日榜 → rank:sales:20240601,TTL 7 天 |
| 数据能否重算? | 决定一致性要求与容错方案 | 能从订单表重算 → 允许最终一致 |
五、本课程将覆盖的能力地图
接下来的章节会沿着"场景 → 结构 → 命令 → 工程化"这条线展开。你会学到如何用 ZADD、ZINCRBY、ZREVRANGE、ZREVRANK 构建排行榜,如何处理同分排序和分页,如何用 INCR 系列命令做高并发计数,如何设计 key 的命名与过期,以及如何应对缓存穿透、热点 key、数据重建等工程问题。本章建立的场景认知,是后面所有技术决策的判断依据。