Redis核心数据结构详解与应用
Redis 的强大核心在于其丰富的数据结构,每种结构都针对特定场景设计,并配有专用的命令集。以下是 Redis 最常用的核心数据结构及其关键特性、应用场景和典型命令:
1. String (字符串)
- 本质: 最简单、最基础的类型。可存储文本、数字(整数/浮点数)或二进制数据(最大 512MB)。
- 底层实现: 通常使用 SDS (Simple Dynamic String),支持高效的长度获取、追加操作和二进制安全。
- 核心特性:
- 原子计数器 (
INCR
,DECR
,INCRBY
,DECRBY
,INCRBYFLOAT
)。 - 位操作 (
SETBIT
,GETBIT
,BITCOUNT
,BITOP
)。 - 设置过期时间 (
SET key value EX seconds
,SETEX
,PSETEX
)。 - 不存在时设置 (
SETNX
- 用于分布式锁基础)。 - 批量设置/获取 (
MSET
,MGET
)。
- 原子计数器 (
- 典型应用场景:
- 缓存 HTML 片段、API 响应结果、序列化对象。
- 计数器(用户点赞数、文章阅读量、库存)。
- 分布式锁(
SET resource_name random_value NX PX milliseconds
)。 - 位图 (Bitmaps) 实现签到、用户状态位图(本质是 String)。
- 存储小图片或文件序列化。
- 常用命令:
SET key value [EX seconds|PX milliseconds] [NX|XX] # 设置值 (可带过期/条件) GET key # 获取值 INCR key # 整数+1 DECR key # 整数-1 INCRBY key increment # 整数增加指定值 APPEND key value # 追加字符串 STRLEN key # 获取字符串长度 GETRANGE key start end # 获取子串 SETBIT key offset value # 设置位 (value 0/1) GETBIT key offset # 获取位 MSET key1 value1 key2 value2 ... # 批量设置 MGET key1 key2 ... # 批量获取
2. Hash (哈希表 / 字典)
- 本质: 一个
field-value
映射表。非常适合存储对象(如用户信息、商品属性)。 - 底层实现: 当元素少且小用 ziplist(压缩列表,节省内存),否则用 hashtable。
- 核心特性:
- 可独立操作单个字段 (
HSET
,HGET
,HDEL
)。 - 批量操作 (
HMSET
,HMGET
,HGETALL
)。 - 字段自增 (
HINCRBY
,HINCRBYFLOAT
)。 - 判断字段存在 (
HEXISTS
)。
- 可独立操作单个字段 (
- 典型应用场景:
- 存储对象(用户信息:
user:1000 {name: "Alice", age: 30, email: ...}
)。 - 购物车(
cart:user123 {productId1: quantity, productId2: quantity}
)。 - 存储具有多个属性的配置项。
- 存储对象(用户信息:
- 优点 (相比 String 存储对象):
- 节省空间: 多个字段存储在一个 Key 下,减少 Key 数量。
- 操作高效: 可单独读写/更新对象的某个属性,无需序列化/反序列化整个对象。
- 常用命令:
HSET key field value # 设置单个字段 HGET key field # 获取单个字段 HMSET key field1 value1 field2 value2 ... # 批量设置字段 (HMSET 在新版本中常被 HSET 替代) HMGET key field1 field2 ... # 批量获取字段 HGETALL key # 获取所有字段和值 (小心大 Hash!) HKEYS key # 获取所有字段名 HVALS key # 获取所有字段值 HDEL key field1 [field2 ...] # 删除字段 HEXISTS key field # 判断字段是否存在 HINCRBY key field increment # 整数字段增加指定值 HLEN key # 获取字段数量
3. List (列表)
- 本质: 一个有序、可重复元素的集合。元素在列表两端插入或弹出。
- 底层实现: 早期用 ziplist (小列表),现在多用 linkedlist (双端链表) 或 quicklist (3.2+,ziplist 组成的双向链表,平衡内存和效率)。
- 核心特性:
- 支持 LIFO (栈) 和 FIFO (队列) 操作。
- 可在头部 (
LPUSH
) 或尾部 (RPUSH
) 插入元素。 - 可在头部 (
LPOP
) 或尾部 (RPOP
) 弹出元素。 - 按索引获取元素 (
LINDEX
)。 - 获取列表片段 (
LRANGE
)。 - 阻塞弹出 (
BLPOP
,BRPOP
- 用于简单消息队列)。
- 典型应用场景:
- 消息队列 (
LPUSH
+BRPOP
/RPOPLPUSH
保证可靠性)。 - 最新消息/文章列表 (
LPUSH
+LTRIM
保留最新 N 条)。 - 记录操作流水。
- 实现栈 (
LPUSH
+LPOP
)。
- 消息队列 (
- 常用命令:
LPUSH key element [element ...] # 从头部插入一个或多个元素 RPUSH key element [element ...] # 从尾部插入一个或多个元素 LPOP key # 从头部弹出元素 RPOP key # 从尾部弹出元素 BLPOP key [key ...] timeout # 阻塞地从头部弹出 (队列) BRPOP key [key ...] timeout # 阻塞地从尾部弹出 (队列) LLEN key # 获取列表长度 LRANGE key start stop # 获取列表片段 (0 -1 表示所有) LINDEX key index # 按索引获取元素 LTRIM key start stop # 修剪列表,只保留指定索引范围内元素 RPOPLPUSH source destination # 从 source 尾部弹出并插入到 destination 头部 (原子操作,用于可靠队列)
4. Set (集合)
- 本质: 一个无序、不重复元素的集合。支持高效的集合运算。
- 底层实现: 元素少且为整数用 intset,否则用 hashtable (value 为 null)。
- 核心特性:
- 添加/删除元素 (
SADD
,SREM
)。 - 判断元素存在 (
SISMEMBER
)。 - 获取所有元素 (
SMEMBERS
- 小心大集合!)。 - 集合运算:
- 交集 (
SINTER
,SINTERSTORE
) - 并集 (
SUNION
,SUNIONSTORE
) - 差集 (
SDIFF
,SDIFFSTORE
)
- 交集 (
- 随机元素 (
SRANDMEMBER
,SPOP
)。
- 添加/删除元素 (
- 典型应用场景:
- 标签系统(文章标签、用户兴趣)。
- 社交关系(共同好友:交集;好友推荐:差集)。
- 唯一性保证(防止重复提交、抽奖参与者)。
- 随机选取(抽奖
SPOP
,随机推荐SRANDMEMBER
)。 - 数据过滤(白名单/黑名单)。
- 常用命令:
SADD key member [member ...] # 添加一个或多个成员 SREM key member [member ...] # 移除一个或多个成员 SISMEMBER key member # 判断成员是否存在 SMEMBERS key # 获取所有成员 (无序) SCARD key # 获取成员数量 SRANDMEMBER key [count] # 随机返回一个或多个成员 (不删除) SPOP key [count] # 随机移除并返回一个或多个成员 SINTER key [key ...] # 计算多个集合的交集 SUNION key [key ...] # 计算多个集合的并集 SDIFF key [key ...] # 计算第一个集合与其他集合的差集 SINTERSTORE destination key [key ...] # 计算交集并存储到 destination
5. Sorted Set (ZSet - 有序集合)
- 本质: 在 Set 的基础上,为每个元素关联一个分数 (
score
)。元素按分数排序(从小到大),分数可相同。元素唯一,分数可更新。 - 底层实现: ziplist (小集合) 或 skiplist (跳跃表) + hashtable。跳跃表保证有序范围查询效率,hashtable 保证按成员查询效率。
- 核心特性:
- 按分数范围查询 (
ZRANGEBYSCORE
,ZREVRANGEBYSCORE
)。 - 按排名(索引)查询 (
ZRANGE
,ZREVRANGE
)。 - 获取成员排名 (
ZRANK
- 正序,ZREVRANK
- 逆序)。 - 获取成员分数 (
ZSCORE
)。 - 增加成员分数 (
ZINCRBY
)。 - 集合运算(
ZUNIONSTORE
,ZINTERSTORE
- 可指定分数聚合方式 sum/min/max)。
- 按分数范围查询 (
- 典型应用场景:
- 排行榜!(按分数排序,如游戏积分榜、商品销量榜、热搜榜)。
- 带权重的队列(分数代表优先级或执行时间戳)。
- 范围查找(时间线、价格区间商品)。
- 限流(滑动窗口,用 ZSet 存储时间戳)。
- 常用命令:
ZADD key [NX|XX] [CH] [INCR] score member [score member ...] # 添加成员及其分数 (可带条件/增量) ZSCORE key member # 获取成员分数 ZRANGE key start stop [WITHSCORES] # 按排名(低->高)获取成员(带分数) ZREVRANGE key start stop [WITHSCORES] # 按排名(高->低)获取成员(带分数) ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count] # 按分数范围(低->高)获取成员 ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count] # 按分数范围(高->低)获取成员 ZRANK key member # 获取成员正序排名 (从0开始) ZREVRANK key member # 获取成员逆序排名 ZINCRBY key increment member # 增加成员分数 ZCARD key # 获取成员数量 ZCOUNT key min max # 计算分数范围内成员数量 ZREM key member [member ...] # 移除成员 ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX] # 并集计算并存储
6. 其他重要/高级数据类型
虽然上述 5 种是最核心的,但 Redis 还提供了一些强大的扩展类型:
- Bitmaps (位图):
- 本质: 通过 String 类型的位操作实现。可视为一个巨大的位数组。
- 应用: 用户在线状态、每日签到记录 (
SETBIT
,GETBIT
,BITCOUNT
,BITOP
- AND/OR/XOR/NOT)。
- HyperLogLog (基数统计):
- 本质: 用于估算一个集合中不重复元素的数量(基数),占用极小固定内存(约 12KB)。
- 应用: 大规模独立访客统计 (UV) (
PFADD
,PFCOUNT
,PFMERGE
)。 - 注意: 有误差(约 0.81%),无法获取具体元素。
- Geospatial Indexes (地理空间索引):
- 本质: 基于 Sorted Set 实现,存储经纬度信息。
- 应用: 附近的人/地点、计算距离 (
GEOADD
,GEOPOS
,GEODIST
,GEORADIUS
,GEORADIUSBYMEMBER
)。
- Streams (流):
- 本质: Redis 5.0+ 引入,为日志类数据设计的持久化消息队列。支持消费者组、消息确认、回溯。
- 应用: 比 List 更可靠的消息队列、事件溯源 (
XADD
,XREAD
,XREADGROUP
,XACK
,XRANGE
,XGROUP
)。
选择数据结构的建议
-
问“我要存什么?”:
- 简单键值/计数器? -> String
- 对象(多个属性)? -> Hash
- 有序列表(最新、队列、栈)? -> List
- 唯一集合(标签、共同好友)? -> Set
- 需要排序(排行榜、优先级)? -> Sorted Set
- 是/否状态(签到)? -> Bitmaps
- 估算大量不重复元素(UV)? -> HyperLogLog
- 地理位置? -> Geospatial
- 可靠消息流? -> Streams
-
考虑性能: 了解常用命令的时间复杂度 (O(1), O(log(N)), O(N))。避免对大集合使用
SMEMBERS
、HGETALL
、KEYS *
(用SCAN
代替)。 -
考虑内存:
ziplist
/intset
在元素少且小时非常节省内存。关注INFO MEMORY
。 -
组合使用: 复杂业务场景可能需要组合多种数据结构(例如,用
Sorted Set
存储排行榜 ID 和分数,用Hash
存储 ID 对应的详细信息)。
理解并熟练掌握这些数据结构及其适用场景,是高效、优雅使用 Redis 的关键! 它们赋予了 Redis 远超简单键值存储的灵活性和强大能力。