Redis 之所以强大和受欢迎,很大程度上是因为它提供了丰富的数据结构,而不仅仅是一个简单的键值存储。这些数据结构让开发者可以直接在存储层解决许多问题,而无需在应用层进行复杂的处理。

Redis 的数据结构可以分为两大类:

  1. 基本数据结构:String(字符串)、List(列表)、Hash(哈希)、Set(集合)、Sorted Set(有序集合)。
  2. 特殊和高级数据结构: Bitmaps(位图)、HyperLogLogs(基数统计)、Geospatial Indexes(地理空间索引)、Streams(流)。这些通常是在基本数据结构之上实现的,但提供了独特的 API 来解决特定问题。

一、基本数据结构

1. String(字符串)

描述:最基本的数据类型,一个 Key 对应一个 Value。它是二进制安全的,意味着可以存储任何数据,比如图片、序列化对象等。Value 最大不能超过 512MB。

常用命令SET, GET, MSET, MGET, INCR, DECR, INCRBY, SETEX(设置过期时间)等。

典型应用场景

  • 缓存(Cache):这是最经典的场景。将数据库查询结果、热点数据、会话信息(Session)等序列化成字符串后存入 Redis,加速访问。
  • 计数器(Counter):利用 INCR, DECR 命令实现点赞数、浏览数、库存扣减等。这些操作是原子性的,无需担心并发问题。
  • 分布式锁(Distributed Lock):使用 SET key value NX EX seconds 命令实现简单的分布式锁(NX 表示只有 key 不存在时才设置,EX 设置过期时间)。
  • Session 共享:在分布式集群中,将用户的 Session 信息统一存储在 Redis 里,实现多台服务器间的状态共享。

2. List(列表)

描述:一个简单的字符串列表,按插入顺序排序。你可以在头部(left)或尾部(right)添加元素。底层实现是双向链表,所以操作头尾元素的速度极快。

常用命令LPUSH, RPUSH, LPOP, RPOP, LRANGE, BLPOP(阻塞式弹出)等。

典型应用场景

  • 消息队列(Message Queue):使用 LPUSH + BRPOP 组合实现一个简单的 FIFO(先进先出)队列。生产者 LPUSH 消息,消费者用 BRPOP 阻塞地获取消息。
  • 最新列表(Timeline):例如朋友圈的最新动态、新闻网站的最新文章列表。使用 LPUSH 将新内容插入列表头部,再用 LRANGE 0 9 获取最新的 10 条。
  • 排行榜(简单版):如果需要实时更新的排行榜,但逻辑不复杂,可以用 List 存储。

3. Hash(哈希)

描述:是一个键值对集合,特别适合用于存储对象。类似于编程语言中的 Mapdict 类型。

常用命令HSET, HGET, HMSET, HMGET, HGETALL, HINCRBY 等。

典型应用场景

  • 存储对象信息:这是最自然的应用。例如存储用户信息(user:1)、商品信息(product:101)。可以单独获取或修改对象的某个字段(如 HGET user:1 name),而不用像 String 那样需要序列化/反序列化整个对象。
  • 购物车:以用户ID为 Key,商品ID为 Field,商品数量为 Value。可以轻松地添加商品、增加数量、获取所有商品。

4. Set(集合)

描述:是 String 类型的无序集合,通过哈希表实现,保证了元素的唯一性(自动去重)。支持高效的集合操作,如交集、并集、差集。

常用命令SADD, SMEMBERS, SISMEMBER, SINTER(交集), SUNION(并集), SDIFF(差集), SPOP(随机弹出)等。

典型应用场景

  • 标签(Tag):例如给用户打标签(SADD user:1:tags tag1 tag2),给文章打标签。可以很方便地找到有相同标签的人或文章(求交集)。
  • 共同关注/好友(Social Graph)SINTER user:A:follows user:B:follows 可以快速计算出 A 和 B 的共同关注。
  • 抽奖/随机事件SADD 添加所有参与者,SMEMBERS 查看所有参与者,SPOP 随机移除并返回一个元素(用于抽奖)。
  • 数据排重:例如对爬取的 URL 进行去重。

5. Sorted Set(有序集合 / ZSet)

描述:与 Set 类似,也是 String 类型元素的集合,且不允许重复。但每个元素都会关联一个 double 类型的分数(score)。Redis 正是通过这个分数来为集合中的成员进行从小到大排序。元素是唯一的,但分数可以重复。

常用命令ZADD, ZRANGE(按分数正序), ZREVRANGE(按分数倒序), ZRANK(获取排名), ZRANGEBYSCORE 等。

典型应用场景

  • 排行榜(Leaderboard):这是最完美的场景。例如游戏玩家积分排行榜、视频热度排名。用 ZADD 更新分数,ZREVRANGE 来获取前十名,ZRANK 来查看某用户的排名。
  • 带权重的队列:分数可以作为优先级,实现优先级队列。
  • 范围查找(Range Query):例如处理延时任务,将任务执行时间作为 score,用 ZRANGEBYSCORE 查询到期的任务。或者统计一段时间内的操作记录。

二、特殊和高级数据结构

1. Bitmaps(位图)

描述:本质上不是一种独立的数据结构,它其实是基于 String 类型的一套面向位的操作方法。可以将 String value 看作是一个由比特位组成的数组,并能对其中任意一位进行置位(set)、清零(get)、计数等操作。

常用命令SETBIT, GETBIT, BITCOUNT, BITOP(位操作)等。

典型应用场景

  • 大量布尔值的存储与统计:例如用户的每日签到记录(1 表示签到,0 表示未签到),一年只需 365 bit ≈ 46 字节。
  • 用户活跃度统计:使用 SETBIT 记录用户是否活跃,BITCOUNT 可以快速统计某段时间内活跃用户数,BITOP AND/OR 可以计算共同活跃天数等。

2. HyperLogLogs

描述:用于做基数统计的算法。它的优点是,在输入元素的数量或者体积非常大时,计算基数所需的空间总是固定且很小的。每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基数。注意:它只会根据输入元素来计算基数,而不会储存输入元素本身

常用命令PFADD, PFCOUNT, PFMERGE

典型应用场景

  • 大规模数据去重统计:例如统计一个网站的 UV(独立访客数)、搜索关键词的不重复数量。它提供的是近似值,有小于 1% 的误差,但对于大数据量且不要求绝对精确的场景非常高效。

3. Geospatial Indexes(地理空间索引)

描述:在 Redis 3.2 版本中推出。用于存储和操作地理位置信息,如经纬度。底层基于 Sorted Set 实现。

常用命令GEOADD, GEOPOS(获取位置), GEODIST(计算距离), GEORADIUS(查找指定半径内的元素)等。

典型应用场景

  • 附近的人/地点(Nearby):例如美团/饿了么查找附近的餐馆,滴滴/ Uber 查找附近的车辆。
  • 计算两地距离GEODIST 可以轻松计算出两个地理位置之间的直线距离。

4. Streams(流)

描述:在 Redis 5.0 中引入的新的强大数据结构。它提供了一个完整的、支持多消费者的消息队列功能,弥补了之前用 List 做消息队列时功能上的不足(如没有消息回溯、消费者组等功能)。

常用命令XADD(添加消息), XREAD(读取消息), XGROUP(创建消费者组)等。

典型应用场景

  • 可靠的消息队列(Message Queue):类似于 Kafka 的日志流,可用于事件溯源、实时消息传输、监控数据采集等场景。它支持消费者组(Consumer Group),确保每条消息能被多个组消费,且在同一个组内只能被一个消费者消费。

总结与选择建议

数据结构 特性 典型场景
String 最简单的键值对 缓存、计数器、分布式锁
List 有序、可重复、双向链表 消息队列、最新列表
Hash 存储对象、可操作单个字段 用户信息、购物车
Set 无序、唯一、支持集合运算 标签、共同好友、抽奖
Sorted Set 有序、唯一、按分数排序 排行榜、延时任务、范围查找
Bitmaps 位操作、极其节省空间 签到统计、用户画像
HyperLogLog 超大基数去重统计、有误差 UV 统计
Geo 地理位置计算 附近的人、计算距离
Streams 持久化的消息流、消费者组 可靠的消息队列

选择哪种数据结构,取决于你的具体业务需求

  • 需要存一个简单的值或做计数器?用 String
  • 需要存一个对象并单独修改其字段?用 Hash
  • 需要保证消息顺序或实现简单队列?用 List
  • 需要去重或求交集并集?用 Set
  • 需要排序和排行榜?用 Sorted Set
  • 需要统计大量的是/否状态?用 Bitmaps
  • 需要估算巨大集合的不重复元素数?用 HyperLogLog
  • 需要处理地理位置?用 Geo
  • 需要构建一个可靠的消息系统?用 Streams