2. Redis 基础回顾:五种核心数据类型及适用场景

在深入企业级热点削峰与审批流优化之前,必须牢固掌握 Redis 的五种核心数据类型。它们是所有高级特性的基石,选错类型会导致性能灾难或架构复杂化。本节从底层结构、操作命令、时间复杂度到企业级场景,逐一拆解。

2.1 数据类型总览与选型速查

下表从底层编码、最大容量、核心操作复杂度三个维度对比五种类型,方便你在架构设计时快速决策。

类型 底层编码(3.2+) 最大元素数 核心操作时间复杂度 典型企业场景
String int / embstr / raw 512 MB(单 value) GET/SET: O(1) 分布式锁、计数器、Session 共享
List quicklist(ziplist + linkedlist) 2³² - 1 个元素 LPUSH/RPOP: O(1);LINDEX: O(N) 消息队列、时间轴、异步任务
Hash ziplist / hashtable 2³² - 1 个 field-value 对 HGET/HSET: O(1);HGETALL: O(N) 对象缓存、用户属性、审批单详情
Set intset / hashtable 2³² - 1 个成员 SADD/SISMEMBER: O(1);SMEMBERS: O(N) 标签系统、去重、共同好友
ZSet ziplist / skiplist + hashtable 2³² - 1 个成员 ZADD: O(log N);ZRANGE: O(log N + M) 排行榜、延时队列、审批优先级
架构师提示: 企业级选型时,优先考虑操作的时间复杂度。例如,List 的 LINDEX 是 O(N),当列表长度超过 1000 时,随机访问性能急剧下降;此时应改用 ZSet 或 Hash 替代。

2.2 String — 最基础但最强大的原子载体

String 是 Redis 中最简单的类型,但企业级 80% 的优化场景都离不开它。底层采用动态字符串(SDS),支持自动扩容和二进制安全。

  • 原子计数器: INCR/DECR 命令是原子操作,适用于秒杀库存扣减、接口限流令牌桶。
  • 分布式锁: SET key value NX EX seconds 是 Redlock 的基础,保证互斥性。
  • 对象序列化缓存: 将 Java POJO 序列化为 JSON/Protobuf 存入,适合用户 Session、配置信息。
  • 位图(Bitmap): SETBIT/GETBIT 实现亿级用户签到、在线状态统计,内存极省。
// 企业级分布式锁示例(Jedis)
String result = jedis.set("lock:order:1001", "thread-1", "NX", "EX", 30);
if ("OK".equals(result)) {
    try {
        // 执行业务逻辑
    } finally {
        // 使用 Lua 脚本保证原子释放
        String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
        jedis.eval(script, 1, "lock:order:1001", "thread-1");
    }
}

2.3 List — 轻量级消息通道与时间轴

List 底层使用 quicklist(3.2+),结合了 ziplist 的内存紧凑和 linkedlist 的双端操作优势。企业级最常用作异步解耦的缓冲队列。

  • 任务队列: LPUSH 生产,BRPOP 阻塞消费,实现生产者-消费者模式。
  • 时间轴(Timeline): LTRIM 截断保留最新 N 条,适合用户动态流。
  • 栈结构: LPUSH + LPOP 实现后进先出,用于回滚操作栈。
避坑: 不要用 List 做“消息确认”或“延迟消费”。List 的 RPOP 一旦取出即删除,没有 ACK 机制。需要可靠消费时,应使用 Stream 或专业的消息队列(如 RabbitMQ)。

2.4 Hash — 对象级缓存的正确姿势

Hash 是 Redis 模仿关系型数据库行存储的最佳结构。相比 String 序列化整个对象,Hash 支持对单个字段进行读写,节省带宽并支持部分更新。

  • 审批单详情缓存: 将审批单 ID 作为 key,字段为 applicant、approver、status、createTime,HGET 按需获取。
  • 用户属性: 用户 ID 为 key,字段为 nickname、avatar、level,HINCRBY 更新积分。
  • 购物车: 用户 ID 为 key,商品 SKU 为 field,数量为 value。
// 审批单部分更新(仅修改状态字段)
jedis.hset("approval:20250301:001", "status", "APPROVED");
// 批量获取审批单关键字段
List<String> fields = jedis.hmget("approval:20250301:001", "applicant", "status", "createTime");

2.5 Set — 去重与集合运算利器

Set 基于哈希表实现,所有成员唯一。企业级最常用的是集合运算(交集、并集、差集),这些运算在服务端完成,避免大量数据回传。

  • 标签系统: 用户 ID 为 key,标签名为 member,SINTER 计算共同标签用户。
  • 抽奖去重: SADD 添加参与用户,SRANDMEMBER 随机抽取,保证不重复中奖。
  • 好友关系: 用户 ID 为 key,好友 ID 为 member,SISMEMBER O(1) 判断是否好友。
性能要点: SMEMBERS 会返回所有成员,当集合超过 1000 个时,应改用 SSCAN 游标迭代,避免阻塞 Redis 单线程。

2.6 ZSet — 排序与优先级的最佳选择

ZSet(有序集合)是 Redis 最复杂也最强大的类型。底层使用跳跃表(skiplist) + 哈希表,兼顾 O(log N) 的插入/删除和 O(1) 的成员查找。企业级审批流中的优先级排队、热点削峰中的延时队列都依赖 ZSet。

  • 审批优先级队列: 以审批单 ID 为 member,紧急程度(如 1-100)为 score,ZRANGEBYSCORE 按优先级处理。
  • 延时任务: 以任务 ID 为 member,执行时间戳为 score,ZRANGEBYSCORE 轮询到期任务。
  • 实时排行榜: 用户 ID 为 member,积分/销售额为 score,ZREVRANGE 获取 Top N。
// 审批流优先级队列:score 越小优先级越高
jedis.zadd("approval:queue", 1, "order-1001");   // 紧急
jedis.zadd("approval:queue", 10, "order-1002");  // 普通
// 获取待处理审批(按优先级升序)
Set<String> urgent = jedis.zrangeByScore("approval:queue", 0, 5);
// 移除已处理的审批
jedis.zrem("approval:queue", "order-1001");

2.7 五种类型的企业级选型决策树

下面的 SVG 图展示了从业务需求到 Redis 数据类型的决策路径,帮助你在架构设计时快速定位。

Redis 数据类型选型决策树 业务需求 需要排序? 不需要排序? 按 Score 排序? 按插入顺序? ZSet List 需要去重? Set 需要部分更新? Hash String 选型决策要点: • 需要按权重/时间排序处理 → ZSet(审批优先级、延时队列) • 需要 FIFO 队列或时间轴 → List(任务缓冲、动态流) • 需要对象部分字段读写 → Hash(审批单详情、用户属性) • 需要唯一性判断或集合运算 → Set(标签、去重) • 简单 KV 或原子计数 → String(分布式锁、计数器)

2.8 企业级场景对照表

下表将课程后续章节涉及的热点削峰与审批流场景直接映射到数据类型,方便你在实战中快速参考。

企业场景 推荐类型 核心命令 为什么选它
秒杀库存扣减 String DECR / INCR 原子操作,单行命令完成扣减与判断
热点 Key 本地缓存 + Redis 回源 String GET / SETEX 简单 KV,配合 TTL 自动过期
审批流任务队列 List LPUSH / BRPOP 阻塞消费,天然支持生产者-消费者
审批单详情缓存 Hash HGET / HSET / HINCRBY 支持字段级读写,节省带宽
审批优先级调度 ZSet ZADD / ZRANGEBYSCORE 按 score 排序,O(log N) 插入与范围查询
延时任务(超时审批) ZSet ZADD / ZRANGEBYSCORE 以时间戳为 score,轮询到期任务
用户标签去重 Set SADD / SINTER 自动去重,服务端集合运算
接口限流计数器 String INCR / EXPIRE 原子自增 + TTL 实现滑动窗口
架构师总结: 五种数据类型不是孤立的,企业级架构中经常组合使用。例如,审批流中同时使用 ZSet(优先级队列)+ Hash(审批详情)+ String(分布式锁),各司其职。掌握每种类型的底层编码和复杂度,是写出高性能 Redis 代码的前提。