Redis 的核心优势在于其丰富的数据结构,这些结构不仅仅是简单的键值存储,而是针对特定场景高度优化的。理解这些结构及其适用场景是高效使用 Redis 的关键。下面详细说明:
Redis 主要数据结构及适用场景
-
String (字符串)
- 描述: 最基本的数据类型。可以存储文本、数字(整数或浮点数)或二进制数据(如图片、序列化对象)。最大可存储 512MB。
- 关键特性:
- 二进制安全: 可以存储任何字节序列。
- 原子操作:
INCR
,DECR
,INCRBY
,DECRFLOAT
等命令是原子的,非常适合计数器。 - 批量操作:
MSET
,MGET
。 - 设置过期时间:
SETEX
,PSETEX
。
- 适用场景:
- 缓存: 缓存 HTML 片段、API 响应、数据库查询结果等。
- 计数器: 网站访问量 (
INCR
)、点赞数 (INCR
)、库存扣减 (DECR
)。 - 分布式锁: 配合
SETNX
(或SET
的NX
选项) 和过期时间实现。 - Session 存储: 存储用户会话信息。
- 简单的键值映射: 存储用户配置项、标志位 (
SET
/GET
)。 - 限速器: 使用
INCR
和EXPIRE
限制 API 调用频率或操作次数。
-
List (列表)
- 描述: 有序的字符串元素集合。元素按插入顺序排序,可以添加在头部 (
LPUSH
) 或尾部 (RPUSH
)。底层实现是双向链表 或 ziplist (小列表优化)。 - 关键特性:
- 双端操作:
LPUSH
/RPUSH
(添加),LPOP
/RPOP
(移除并返回),LRANGE
(获取范围)。 - 阻塞操作:
BLPOP
/BRPOP
/BRPOPLPUSH
(在列表为空时阻塞客户端,直到有元素可弹出)。 - 长度操作:
LLEN
。
- 双端操作:
- 适用场景:
- 消息队列:
- 简单队列:生产者
LPUSH
消息,消费者RPOP
(或BRPOP
) 消息。 - 工作队列:配合
BRPOPLPUSH
实现安全的工作队列(处理中的消息移到另一个列表)。
- 简单队列:生产者
- 最新消息/动态流:
LPUSH
新消息,用LRANGE 0 N
获取最新的 N 条消息 (如 Twitter 时间线)。 - 历史记录:
LPUSH
用户操作记录,用LTRIM
限制长度 (如浏览历史)。 - 栈:
LPUSH
+LPOP
。 - 队列:
LPUSH
+RPOP
(或RPUSH
+LPOP
)。
- 消息队列:
- 描述: 有序的字符串元素集合。元素按插入顺序排序,可以添加在头部 (
-
Set (集合)
- 描述: 无序的、唯一的字符串元素集合。不允许重复元素。底层实现是 hashtable 或 intset (纯整数小集合优化)。
- 关键特性:
- 添加/删除/检查存在:
SADD
,SREM
,SISMEMBER
。 - 集合运算:
SINTER
(交集),SUNION
(并集),SDIFF
(差集),SINTERSTORE
/SUNIONSTORE
/SDIFFSTORE
(存储运算结果)。 - 随机元素:
SRANDMEMBER
,SPOP
(随机移除并返回)。 - 成员操作:
SMEMBERS
(获取所有成员 - 小心大集合!),SCARD
(获取成员数量)。
- 添加/删除/检查存在:
- 适用场景:
- 标签系统: 给对象(文章、用户)打标签 (
SADD
),查找拥有某标签的所有对象 (SMEMBERS
),查找同时拥有多个标签的对象 (SINTER
)。 - 社交关系: 存储用户的粉丝 (
SADD
)、关注 (SADD
) 列表。计算共同关注 (SINTER
)。 - 唯一值追踪: 记录访问过某资源的唯一用户 ID/IP (
SADD
,SISMEMBER
),统计 UV (SCARD
)。 - 抽奖/随机选取:
SPOP
或SRANDMEMBER
。 - 黑名单/白名单: 快速检查元素是否在集合中 (
SISMEMBER
)。
- 标签系统: 给对象(文章、用户)打标签 (
-
Hash (哈希表 / 字典)
- 描述: 键值对集合。
field
(字段名) 和value
(字段值) 都是字符串类型。非常适合表示对象。底层实现是 hashtable 或 ziplist (小哈希优化)。 - 关键特性:
- 字段操作:
HSET
/HGET
(单个),HMSET
/HMGET
(多个),HDEL
。 - 原子字段更新:
HINCRBY
,HINCRBYFLOAT
。 - 检查存在:
HEXISTS
。 - 获取所有/部分:
HGETALL
(小心大哈希!),HKEYS
,HVALS
,HSCAN
(迭代)。 - 长度:
HLEN
。
- 字段操作:
- 适用场景:
- 缓存对象: 比将整个对象序列化成 String 更高效。可以单独更新对象的某个字段 (
HSET
) 而不需要读写整个对象。例如用户信息 (user:id
->{name: "Alice", age: 30, email: ...}
)。 - 存储配置项: 多个相关的配置项可以存储在一个 Hash 里。
- 购物车:
user:cart
->{product1_id: quantity, product2_id: quantity, ...}
。方便增减数量 (HINCRBY
)。 - 需要频繁读写部分属性的场景。
- 缓存对象: 比将整个对象序列化成 String 更高效。可以单独更新对象的某个字段 (
- 描述: 键值对集合。
-
Sorted Set (有序集合 / ZSet)
- 描述: 与 Set 类似,也是唯一的字符串元素集合。但每个元素都关联一个
score
(分数,浮点数)。元素根据score
从小到大排序。如果score
相同,则按元素的字典序排序。底层实现是 skiplist (跳跃表) + hashtable 或 ziplist (小集合优化)。 - 关键特性:
- 添加/更新:
ZADD
(可设置/更新分数)。 - 按分数范围查询:
ZRANGEBYSCORE
,ZREVRANGEBYSCORE
(逆序)。 - 按排名范围查询:
ZRANGE
,ZREVRANGE
(逆序)。 - 按成员查询分数/排名:
ZSCORE
,ZRANK
(正序排名),ZREVRANK
(逆序排名)。 - 集合运算:
ZUNIONSTORE
,ZINTERSTORE
(可聚合分数 - 如 SUM, MIN, MAX)。 - 删除:
ZREM
,ZREMRANGEBYRANK
,ZREMRANGEBYSCORE
。 - 统计数量:
ZCARD
(总数量),ZCOUNT
(分数范围内的数量)。
- 添加/更新:
- 适用场景:
- 排行榜: 经典场景!分数代表热度、分数、时间戳等。
ZADD
更新分数,ZREVRANGE 0 N WITHSCORES
获取 Top N。 - 带权重的队列: 任务优先级队列。分数表示优先级。
- 时间轴: 分数存储时间戳,
ZRANGEBYSCORE
按时间范围查询事件。 - 范围查找: 查找分数在某个区间的所有元素。
- 滑动窗口限流: 将请求时间戳作为分数加入 ZSet,定期删除旧于时间窗口的元素,检查窗口内元素数量是否超限。
- 排行榜: 经典场景!分数代表热度、分数、时间戳等。
- 描述: 与 Set 类似,也是唯一的字符串元素集合。但每个元素都关联一个
-
Bitmaps (位图)
- 描述: 不是独立的数据类型,而是基于 String 类型提供的一组面向位的操作命令。可以将 String 视为一个由二进制位组成的数组。
- 关键特性:
- 位操作:
SETBIT
(设置位),GETBIT
(获取位),BITCOUNT
(统计置位数量),BITOP
(位运算 AND/OR/XOR/NOT),BITPOS
(查找第一个置位/清零位)。 - 高效存储: 非常适合存储布尔值信息(是/否,存在/不存在)。
- 位操作:
- 适用场景:
- 用户在线状态: 用户 ID 作为偏移量 (
offset
),SETBIT online_status
。 - 签到系统: 用户 ID + 日期作为键,偏移量代表具体日期(一年第几天)。
SETBIT sign:2024:userid
。 - 特征标志: 用户拥有哪些特性/权限 (每个位代表一个特性)。
- 布隆过滤器 (Bloom Filter) 基础: 需要结合多个 Bitmap 和哈希函数实现。
- 实时分析: 高效统计大量布尔属性(如活跃用户)。
- 用户在线状态: 用户 ID 作为偏移量 (
-
HyperLogLog (基数统计)
- 描述: 用于估计一个集合中唯一元素数量 (基数)的概率性数据结构。不是独立类型,有专用命令(
PFADD
,PFCOUNT
,PFMERGE
)。 - 关键特性:
- 极省内存: 无论元素数量多少,固定使用约 12KB 内存。
- 非精确: 提供的是带有标准误差(约 0.81%)的估计值。
- 不支持获取元素: 只能添加元素和统计基数。
- 适用场景:
- 海量数据去重计数: 网站每日独立访客数 (UV)、搜索引擎独立搜索词统计、大型集合的近似基数计算。当精确结果不是必需且需要节省大量内存时使用。
- 描述: 用于估计一个集合中唯一元素数量 (基数)的概率性数据结构。不是独立类型,有专用命令(
-
Geospatial Indexes (地理空间索引)
- 描述: 基于 Sorted Set (ZSet) 实现的,用于存储地理位置(经度、纬度)并执行相关查询的数据结构。有专用命令(
GEOADD
,GEODIST
,GEORADIUS
,GEORADIUSBYMEMBER
,GEOHASH
)。 - 关键特性:
- 添加位置:
GEOADD
(本质上是将经纬度编码成score
存入 ZSet)。 - 计算距离:
GEODIST
。 - 范围查询:
GEORADIUS
(以给定点为中心,半径内的元素),GEORADIUSBYMEMBER
(以某个成员为中心)。 - 获取 Geohash:
GEOHASH
。
- 添加位置:
- 适用场景:
- 附近的人/地点: 查找用户当前位置一定半径范围内的 POI(兴趣点)、司机、朋友等。
- 距离计算: 计算两个地理位置之间的距离。
- 基于位置的推荐。
- 描述: 基于 Sorted Set (ZSet) 实现的,用于存储地理位置(经度、纬度)并执行相关查询的数据结构。有专用命令(
-
Streams (流)
- 描述: Redis 5.0 引入的,专为消息传递和事件溯源设计的数据结构。类似于 Kafka 或 RabbitMQ 的持久化消息队列。
- 关键特性:
- 消息持久化与多播: 支持持久化消息,多个消费者组可以独立消费。
- 结构化消息: 每条消息由键值对组成。
- 消费者组: 提供消费者组 (
XGROUP
), 消费者 (XREADGROUP
), 消息确认 (XACK
), 待处理消息处理 (XPENDING
,XCLAIM
) 等机制。 - 范围查询:
XRANGE
,XREVRANGE
。 - 阻塞读取:
XREAD
/XREADGROUP
支持阻塞等待新消息。
- 适用场景:
- 可靠的消息队列: 需要持久化、多消费者组、消息确认的场景。
- 事件溯源: 记录系统中发生的事件序列。
- 实时数据管道: 连接不同服务或微服务。
- 日志采集与分发。
选择数据结构的总结建议
- 简单键值/缓存/计数器? -> String
- 队列/栈/最新列表? -> List
- 无序唯一集合/标签/社交关系? -> Set
- 对象缓存/购物车/需要更新部分字段? -> Hash
- 排行榜/带权重队列/时间轴/范围查询? -> Sorted Set (ZSet)
- 布尔值/特征标志/签到/在线状态? -> Bitmaps (基于 String)
- 海量数据去重计数 (近似值)? -> HyperLogLog
- 地理位置/附近搜索? -> Geospatial (基于 ZSet)
- 可靠消息队列/事件溯源? -> Streams
重要注意事项
- 内存是核心资源: Redis 数据存储在内存中,选择合适的数据结构对控制内存占用至关重要(如使用 Hash 代替多个 String,使用 HyperLogLog 代替 Set 计数)。
- 时间复杂度: 了解主要命令的时间复杂度(如
SMEMBERS
是 O(n),大集合慎用;ZRANGE
是 O(log N + M),M 是返回元素数)。使用SCAN
、HSCAN
、SSCAN
、ZSCAN
替代KEYS
、HGETALL
、SMEMBERS
等遍历命令避免阻塞。 - 持久化: 根据需求配置 RDB 快照或 AOF 日志,确保数据安全。
- 集群与分片: 单实例容量有限,大数据量需要 Redis Cluster 或客户端分片。