Redis数据结构
ps:如果这篇帖子对于还在找工作和找实习的你有所帮助,可以关注我,给本贴点赞、评论、收藏并订阅专栏;同时不要吝啬您的花花
Redis 作为高性能的键值对数据库,支持多种灵活的数据结构,每种结构都有其独特的底层实现和适用场景,熟练掌握这些数据结构及其底层原理是高效使用 Redis 的关键。以下是 Redis 核心基础数据结构、常用扩展数据结构的详细解析,重点明确每种结构底层使用的数据结构,覆盖 string、hash、list、set、zset、bitmap、geo、HyperLogLog、stream 所有指定类型。
一、基础核心数据结构(明确底层数据结构)
1. 字符串(String)
String 是 Redis 最基础、最常用的数据结构,也是其他数据结构的基础,它可以存储字符串、数字(整数/浮点数)、二进制数据(如图片、序列化对象),最大存储容量为 512MB。
底层实现:默认使用简单动态字符串(SDS) 实现,而非 C 语言原生字符串。SDS 具备动态扩容、减少内存碎片、二进制安全(可存储任意二进制数据,如图片、视频流)等优势,同时兼容 C 语言字符串操作函数,是 Redis 中 String 类型的核心底层支撑。
SDS 扩容与缩容时机:
1. 扩容时机:当执行 append、set 等命令,需要向 SDS 追加内容,且 SDS 剩余空闲空间(free 字段)不足以容纳新增内容时,触发扩容。扩容规则遵循“空间预分配”策略,避免频繁扩容:
- 若扩容后 SDS 总长度 < 1MB,扩容后的空闲空间与实际存储长度(len 字段)相等,即总容量 = len + free(free = len);
- 若扩容后 SDS 总长度 ≥ 1MB,扩容后的空闲空间固定为 1MB,避免因频繁追加导致内存浪费;
- SDS 最大容量为 512MB,达到该上限后无法继续扩容,会返回错误。
2. 缩容时机:SDS 不支持自动缩容,当执行 trim 等命令手动删除部分内容后,空闲空间会增加,但不会自动释放多余内存(即 len 减小,free 增大,总容量不变)。若需释放多余内存,需手动执行相关命令(如 redis-cli 的 memory purge 或通过代码调用 SDS 相关 API),主动回收未使用的空间,减少内存碎片。
SDS 与 C 语言字符串、Java 字符串的核心区别:SDS 是 Redis 为解决传统字符串缺陷设计的动态字符串,与 C 语言原生字符串、Java 字符串在底层结构、功能特性上差异显著,具体对比如下:
1. 底层结构差异
- SDS:由 len(实际存储字符串长度)、free(空闲空间长度)、buf(字节数组,存储字符串内容)三部分组成,末尾自动添加 '\0' 以兼容 C 语言字符串操作,但 '\0' 不计算在 len 内。
- C 语言字符串:仅由字节数组(buf)组成,无长度字段,以 '\0' 作为字符串结束标志,无法直接获取字符串长度。
- Java 字符串:底层基于 char 数组(JDK 1.8 及之前)或 byte 数组(JDK 1.9 及之后,根据编码自适应),包含长度字段,且字符串对象不可变(底层数组被 final 修饰)。
2. 长度获取效率
- SDS:直接读取 len 字段,时间复杂度 O(1),无需遍历字节数组。
- C 语言字符串:需遍历字节数组,直到找到 '\0' 才停止,时间复杂度 O(n),n 为字符串长度。
- Java 字符串:直接读取内部长度字段,时间复杂度 O(1),与 SDS 类似,但因字符串不可变,长度一旦确定无法修改。
3. 二进制安全性
- SDS:二进制安全,允许存储任意二进制数据(如图片、视频流),因为其通过 len 字段判断字符串结束,而非 '\0',避免了 '\0' 被误判为结束标志的问题。
- C 语言字符串:非二进制安全,若字符串中包含 '\0',会被误判为字符串结束,导致后续数据丢失,无法存储二进制数据。
- Java 字符串:理论上支持二进制数据(通过 getBytes() 方法),但设计初衷是存储字符,且字符串不可变,频繁修改二进制数据会产生大量临时对象,效率较低。
4. 扩容与修改特性
- SDS:支持动态扩容(遵循空间预分配策略)和手动缩容,修改字符串(如 append)时无需手动管理内存,且不会造成内存溢出。
- C 语言字符串:固定长度,修改时需手动重新分配内存(如 strcat 函数),若内存分配不足会导致溢出,且频繁扩容会产生大量内存碎片。
- Java 字符串:不可变,修改字符串(如 concat、replace)时不会修改原对象,而是创建一个新的字符串对象,频繁修改会占用大量内存,效率较低(需借助 StringBuilder/StringBuffer 优化)。
5. 内存管理
- SDS:手动管理内存(Redis 内核负责分配与释放),支持空闲空间预分配和手动缩容,内存利用率较高。
- C 语言字符串:完全由开发者手动分配和释放内存,容易出现内存泄漏、内存溢出等问题。
- Java 字符串:由 JVM 垃圾回收机制自动管理内存,无需开发者手动操作,但不可变性导致内存利用效率在频繁修改场景下较低。
总结:SDS 兼顾了 C 语言字符串的兼容性和 Java 字符串的高效长度获取,同时解决了两者在二进制安全、内存管理、动态修改上的缺陷,是适配 Redis 高性能、多场景需求的专属字符串实现。
核心命令:
- set key value:设置键值对,覆盖已有键;
- get key:获取指定键的值;
- append key value:在指定键的字符串末尾追加内容;
- incr key / decr key:对存储的整数进行自增/自减操作(原子性);
- setex key seconds value:设置键值对并指定过期时间(秒)。
应用场景:缓存热点数据(如用户信息、商品详情)、计数器(文章阅读量、点赞数)、分布式锁(配合 setnx 命令)、存储二进制数据(如验证码图片)。
2. 哈希(Hash)
Hash 是一种键值对的集合,适合存储一个对象的多个属性(如用户信息:姓名、年龄、手机号),支持对单个属性的增删改查,无需操作整个对象,效率更高。
底层实现:采用双底层结构自适应,根据元素数量和大小动态切换:
- 哈希表中元素较少(默认少于512个)、且元素值较小时,使用 ziplist(压缩列表)存储,节省内存;
- 当元素数量或值大小超过阈值时,自动转为 hashtable(哈希表),保证查询、修改的高效性(时间复杂度接近 O(1))。
补充:ziplist(压缩列表)底层结构与存储方式:ziplist 是 Redis 设计的一种紧凑的顺序存储结构,核心目的是节省内存,适用于元素数量少、元素值小的场景(如 Hash、List 元素较少时)。
底层结构(从前往后):由 4 个固定头部 + 多个 entry(元素节点) + 1 个尾部标识组成,所有数据连续存储在一块内存中,无冗余指针,极大减少内存碎片:
- zlbytes:4 字节,记录整个 ziplist 的总字节数,用于快速计算 ziplist 末尾位置;
- zltail:4 字节,记录 ziplist 中最后一个 entry 的偏移量,无需遍历即可快速定位最后一个元素;
- zllen:2 字节,记录 entry 的数量,若元素数量超过 65535,则该字段失效,需遍历整个 ziplist 统计数量;
- entry:可变长度,存储具体元素,每个 entry 包含“前一个 entry 长度”“元素编码”“元素值”三部分,通过编码区分元素类型(字符串/整数)和长度,实现紧凑存储;
- zlend:1 字节,固定值 0xFF,标记 ziplist 结束。
存储方式:采用连续内存块存储,元素紧密排列,无指针开销;通过“前一个 entry 长度”实现双向遍历(可从头部到尾部,也可从尾部到头部);元素值会根据类型和长度进行编码压缩,进一步节省内存。
ziplist 切换阈值(Hash场景):ziplist 仅适用于元素数量少、值小的场景,超过以下阈值会自动切换为对应结构,阈值支持通过配置文件自定义,默认值如下:
- Hash 场景:当 Hash 中元素数量 ≥ 512 个(配置项 hash-max-ziplist-entries),或任意一个 field/value 字节数 ≥ 64 字节(配置项 hash-max-ziplist-value),触发 ziplist → hashtable 切换;
补充:hashtable(哈希表)底层结构与存储方式:hashtable 是 Redis 中用于快速查询的核心结构,适用于元素数量多、需高频查询/修改的场景(如 Hash、Set 元素较多时),Redis 中的 hashtable 采用“数组 + 链表/红黑树”的组合结构(Redis 7.0 后,链表长度超过阈值会转为红黑树)。
底层结构:由 hashtable 结构体和 dictEntry 结构体组成:
- hashtable 结构体:包含一个 dictEntry 类型的数组(称为“桶”)、桶的数量(size)、已存储元素数量(used)、负载因子阈值等信息;
- dictEntry 结构体:每个桶对应一个 dictEntry 链表(或红黑树),每个 dictEntry 存储一个键值对,包含“键指针”“值指针”“下一个 dictEntry 指针”(用于解决哈希冲突)。
存储方式:通过哈希函数将键(key)转换为哈希值,再对桶的数量取模,得到该键值对对应的桶索引;将 dictEntry 节点插入该桶的链表(或红黑树)中;若多个键哈希后落到同一个桶,形成哈希冲突,通过“链地址法”解决(将冲突节点串联成链表,Redis 7.0 后链表长度超过 8 转为红黑树,提升查询效率)。
hashtable 切换与 rehash 阈值:hashtable 作为切换后的目标结构,无反向切换逻辑,其自身会通过 rehash 动态调整容量,核心阈值基于负载因子(负载因子 = 已存储元素数量 / 哈希表桶数量):
- 扩容阈值:负载因子 ≥ 1 且 Redis 未执行 BGSAVE/BGREWRITEAOF(后台持久化),或负载因子 ≥ 5(强制扩容),扩容后桶数量为原容量的最小2的幂倍数,且大于已存储元素数量×2;
- 缩容阈值:负载因子 ≤ 0.1,缩容后桶数量为不小于已存储元素数量的最小2的幂倍数;
- 补充:Redis 7.0 后,hashtable 中单个桶的链表长度超过 8 时,会转为红黑树,提升冲突元素的查询效率。
Hash 结构 rehash 操作详解:当 Hash 底层为 hashtable 时,为解决哈希冲突、平衡内存与效率,会通过 rehash(重新哈希)动态调整 hashtable 容量,Redis 采用渐进式 rehash 机制,避免一次性迁移数据导致的服务阻塞,具体操作流程如下:
- rehash 触发条件(基于负载因子判断,负载因子 = 已存储元素数量 / hashtable 容量):
- 扩容触发:负载因子 ≥ 1 且 Redis 未执行 BGSAVE/BGREWRITEAOF(后台持久化操作),或负载因子 ≥ 5(强制扩容,无论是否处于持久化);扩容后新容量为原容量的最小2的幂倍数,且满足新容量 > 已存储元素数量 × 2;
- 缩容触发:负载因子 ≤ 0.1 时,触发缩容;缩容后新容量为已存储元素数量的最小2的幂倍数,且保证新容量 ≥ 已存储元素数量,避免空间浪费。
- 渐进式 rehash 完整流程:
- 初始化:Redis 的 hashtable 由两个哈希表(ht[0]、ht[1])组成,平时仅使用 ht[0];触发 rehash 后,为 ht[1] 分配新容量并初始化;
- 渐进迁移:维护一个 rehashidx 计数器(初始为0),标记当前迁移进度;每次执行 Hash 相关命令(增删改查)时,除完成当前操作外,顺带将 ht[0] 中 rehashidx 对应索引桶的所有元素迁移到 ht[1],迁移完成后 rehashidx 自增;同时 Redis 会在定时任务中批量迁移部分元素,加速迁移进程;
- 操作兼容:rehash 期间,查询操作会先查 ht[0],未找到再查 ht[1];插入操作直接写入 ht[1],避免 ht[0] 元素继续增加,加速迁移;删除、修改操作会根据元素所在的哈希表(ht[0] 或 ht[1])执行对应操作,保证数据一致性;
- 完成切换:当 ht[0] 所有元素迁移至 ht[1] 后,释放 ht[0] 内存,将 ht[1] 赋值给 ht[0],重置 ht[1] 为空表,rehashidx 设为 -1,标记 rehash 结束。
- 核心优势:渐进式 rehash 将数据迁移压力分摊到多次命令操作和定时任务中,每次迁移耗时极短,避免了传统一次性 rehash 导致的服务阻塞,兼顾了效率与可用性。
核心命令:
- hset key field value:给指定哈希键设置一个属性和值;
- hget key field:获取指定哈希键的某个属性值;
- hgetall key:获取指定哈希键的所有属性和值;
- hdel key field:删除指定哈希键的某个属性;
- hexists key field:判断指定哈希键是否存在某个属性。
应用场景:存储对象类数据(用户信息、商品属性、订单详情),避免将对象序列化后存储为 String(减少序列化开销,支持单个属性修改)。
3. 列表(List)
List 是一个有序的字符串列表,支持从两端插入、删除元素,可实现先进先出(FIFO)或先进后出(LIFO)的队列/栈操作,元素可重复。
底层实现:同样采用 双底层结构自适应,兼顾内存和效率:
- 当列表中元素较少(默认少于512个)、元素值较小时,使用 ziplist(压缩列表) 存储;
- 当元素数量超过阈值时,自动转为 quicklist(快速列表)——由多个 ziplist 组成的双向链表,既减少内存碎片,又保证两端操作的高效性。
补充:quicklist(快速列表)底层结构与存储方式:quicklist 是 Redis 3.2 后引入的结构,用于替代 List 底层的“ziplist + 双向链表”组合,核心是“分段压缩 + 双向链表”,兼顾内存节省和操作效率。
底层结构:由 quicklist 结构体和 quicklistNode 结构体组成:
- quicklist 结构体:维护整个快速列表的头部指针、尾部指针、元素总数量、quicklistNode 节点数量等信息;
- quicklistNode 结构体:每个节点对应一个 ziplist(称为“片段”),包含“前一个节点指针”“后一个节点指针”“当前节点的 ziplist 指针”“ziplist 字节数”“ziplist 元素数量”等信息;每个 ziplist 片段的元素数量可配置(默认每个片段最多 8192 个元素)。
存储方式:将 List 中的元素分段存储到多个 ziplist 中,每个 ziplist 作为 quicklistNode 的一个片段;多个 quicklistNode 通过双向指针串联成链表,实现 List 的有序性;每个 ziplist 内部采用紧凑存储,节省内存,而 quicklistNode 之间的双向链表则保证了两端插入、删除的高效性(无需遍历所有元素)。
List 场景:当 List 中元素数量 ≥ 512 个(配置项 list-max-ziplist-entries),或任意一个元素字节数 ≥ 64 字节(配置项 list-max-ziplist-value),触发 ziplist → quicklist 切换;
quicklist 核心配置阈值(无切换,仅控制内部片段):quicklist 是 List 切换后的默认结构,无反向切换逻辑,其内部 ziplist 片段的大小可通过配置控制,默认值如下:
- 配置项 list-max-ziplist-size:默认值 -2(对应 8KB),限制每个 quicklistNode 中 ziplist 片段的最大大小(负数表示固定字节数,正数表示最大元素数量);
- 配置项 list-compress-depth:默认值 0,表示不压缩;正数表示从 quicklist 两端向中间跳过的节点数,剩余节点会进行压缩存储,进一步节省内存。
核心命令:
- lpush key value1 value2...:从列表左侧插入一个或多个元素;
- rpush key value1 value2...:从列表右侧插入一个或多个元素;
- lpop key:从列表左侧弹出一个元素;
- rpop key:从列表右侧弹出一个元素;
- lrange key start end:获取列表中从 start 到 end 索引的元素(0 表示第一个元素,-1 表示最后一个元素)。
应用场景:消息队列(简单的生产消费模型)、最新消息展示(如朋友圈动态、评论列表)、栈/队列实现(如任务队列)。
4. 集合(Set)
Set 是一个无序的字符串集合,元素不可重复,支持集合间的交集、并集、差集操作,判断元素是否存在的时间复杂度为 O(1)。
底层实现:根据元素类型和数量自适应,分为两种:
- 当集合中元素均为整数、且数量较少(默认少于512个)时,使用 intset(整数集合) 存储,极大节省内存
- 当元素包含非整数、或数量超过阈值时,使用 hashtable(哈希表) 存储,其中键是集合元素,值为 null(仅用于判断元素是否存在)。
补充:intset(整数集合)底层结构与存储方式:intset 是 Redis 专门为存储整数类型元素设计的紧凑结构,适用于 Set 中元素均为整数且数量较少的场景,核心是“有序、无重复、紧凑存储”。
底层结构:由 3 个固定头部 + 整数数组组成,所有数据连续存储在一块内存中,无冗余开销:
- encoding:4 字节,记录整数的编码方式,分为三种(INTSET_ENC_INT16:存储 16 位整数,范围 -32768~32767;INTSET_ENC_INT32:32 位整数;INTSET_ENC_INT64:64 位整数);
- length:4 字节,记录整数集合中元素的数量;
- contents:可变长度的整数数组,存储具体的整数元素,数组内元素按升序排列,且无重复;数组的编码方式由 encoding 决定,所有元素使用同一编码。
存储方式:采用连续内存块存储整数数组,元素按升序排列,便于快速查找(二分查找,时间复杂度 O(log n));当插入新元素时,若新元素的编码比当前 encoding 更宽(如当前是 16 位,插入 32 位整数),会先对整个数组进行编码升级,再插入元素;元素无重复,插入时会先判断是否存在,避免重复存储。
intset 切换阈值(Set 场景):intset 仅适用于 Set 中元素均为整数且数量少的场景,满足以下条件之一会触发切换为 hashtable,默认阈值如下:
- 数量阈值:Set 中整数元素数量 ≥ 512 个(配置项 set-max-intset-entries);
- 类型阈值:向 intset 中插入非整数类型元素(如字符串、浮点数),无论数量多少,立即触发切换;
- 切换特性:切换为一次性操作,切换后结构不可逆,原 intset 内存会被立即释放。
核心命令:
- sadd key member1 member2...:向集合中添加一个或多个元素(重复元素会自动去重);
- smembers key:获取集合中的所有元素;
- sismember key member:判断元素是否在集合中;
- srem key member:从集合中删除指定元素;
- sinter key1 key2:求两个集合的交集(共同元素);
- sunion key1 key2:求两个集合的并集(所有元素,去重);
- sdiff key1 key2:求两个集合的差集(key1 中有、key2 中没有的元素)。
应用场景:去重(如用户签到记录、商品标签去重)、好友关系(共同好友、好友推荐)、抽奖活动(随机抽取集合中的元素)。
5. 有序集合(Sorted Set / ZSet)
ZSet 是 Set 的增强版,元素不可重复,但每个元素会关联一个“分数(score)”,Redis 会根据分数对元素进行升序或降序排序,兼顾了集合的去重和有序性。
底层实现:采用 跳表(skiplist)+ 哈希表 组合实现,两者协同工作:
- 哈希表:用于存储元素与分数的映射关系,可快速查询指定元素的分数(时间复杂度 O(1));
跳表:用于根据分数对元素进行排序,支持快速获取排序后的元素、范围查询(时间复杂度 O(log n)),兼顾排序和查询效率。
补充:跳表(skiplist)底层结构与时间复杂度详解:跳表是一种有序的数据结构,核心是通过“分层索引”实现高效的查找、插入、删除操作,Redis 中的跳表是 ZSet 排序功能的核心支撑,结构设计简洁且性能优异。
1. 跳表底层结构:跳表由“表头 + 多层链表”组成,每层链表都是有序的(按 ZSet 元素分数升序排列),底层为原始链表(存储所有元素),上层为索引链表(用于快速定位),具体结构如下:
- 表头(header):包含所有层级的索引指针,指向每一层链表的第一个节点;
- 节点(node):每个节点包含“元素值(ZSet 中的 member)”“分数(score)”“向下一层的指针”“同一层的下一个节点指针”;
- 层级(level):每个节点的层级是随机生成的(Redis 中默认最大层级为 32),层级越高,节点数量越少,索引效率越高;底层链表(层级 1)包含所有 ZSet 元素,上层链表(层级 ≥2)为索引,仅存储部分节点,用于快速跳过无效元素。
2. 核心操作与时间复杂度:跳表的所有操作均基于“分层查找”逻辑,时间复杂度均为 O(log n)(n 为跳表中元素数量),具体如下:
- 查找操作:从表头的最高层级开始,依次与当前层级的下一个节点分数对比,若下一个节点分数小于目标分数,则继续前进;若大于目标分数,则下降一层继续查找,直至底层链表,找到目标元素。整个过程类似“二分查找”,通过上层索引快速缩小查找范围,时间复杂度 O(log n);
- 插入操作:先通过查找操作确定插入位置,随机生成当前节点的层级,然后将节点插入到对应层级的链表中,同时更新各层级的指针(保证链表有序),时间复杂度 O(log n)(查找插入位置 O(log n),插入操作 O(1));
- 删除操作:先通过查找操作定位到目标节点,然后删除该节点在所有层级的对应位置,更新各层级的指针,避免链表断裂,时间复杂度 O(log n)(查找目标节点 O(log n),删除操作 O(1));
- 范围查询操作:如 ZSet 的 zrange、zrangebyscore 命令,通过跳表的分层索引快速定位到范围起始位置,然后沿底层链表依次遍历获取所有符合条件的元素,时间复杂度 O(log n + k)(k 为符合条件的元素数量),效率远高于普通链表的 O(n)。
3. 优势说明:跳表的时间复杂度与红黑树相当(均为 O(log n)),但跳表的插入、删除操作实现更简单,无需像红黑树那样进行复杂的旋转调整,且范围查询效率更优,这也是 Redis 选择跳表作为 ZSet 排序核心的关键原因。
核心命令:
- zadd key score1 member1 score2 member2...:向有序集合中添加元素及对应分数;
- zrange key start end [withscores]:按分数升序获取从 start 到 end 的元素(可携带分数);
- zrevrange key start end [withscores]:按分数降序获取元素;
- zscore key member:获取指定元素的分数;
- zrem key member:删除有序集合中的指定元素;
- zrangebyscore key min max:获取分数在 [min, max] 范围内的元素。
应用场景:排行榜(如游戏战力榜、文章热度榜)、带权重的消息队列、范围查询(如查询分数在 80-100 之间的用户)。
二、常用扩展数据结构(明确底层基于的基本数据结构)
1. 位图(Bitmap)
Bitmap 并非独立的数据结构,底层基于 String 数据结构实现,本质是对 String 类型的二进制位进行操作。每个字节的 8 个比特位可表示一个布尔值(0 或 1),适合存储大量的布尔型数据,极大节省内存。
核心命令:setbit(设置指定位置的比特位)、getbit(获取指定位置的比特位)、bitcount(统计比特位为 1 的数量)、bitop(对多个 Bitmap 进行位运算)。
应用场景:用户签到(1 表示签到,0 表示未签到)、在线状态(1 在线,0 离线)、统计活跃用户、连续签到天数统计。
2. 基数统计(HyperLogLog)
HyperLogLog 用于统计一个集合中“不重复元素的个数”(基数统计),底层基于 String 数据结构实现,通过存储基数估算所需的比特串(固定约 12KB 内存),实现海量数据的基数统计,即使统计上亿条数据,内存占用也不会增加,牺牲少量精度(误差在 0.81% 以内)。
核心命令:pfadd(向 HyperLogLog 中添加元素)、pfcount(统计基数)、pfmerge(合并多个 HyperLogLog 实例,统计合并后的基数)。
应用场景:网站 UV 统计(独立访客数)、APP 日活/月活统计、海量数据去重计数(如用户浏览记录去重统计)。
3. 地理信息(Geo)
Geo 用于存储和操作地理坐标数据(如经纬度),支持距离计算、范围查询等功能,底层基于 ZSet 数据结构实现——将经纬度通过特定算法转换为 ZSet 的“分数(score)”,元素为地理位置标识,借助 ZSet 的排序特性实现地理相关操作。
核心命令:
- geoadd key longitude latitude member:添加地理坐标(经度、纬度、地理位置标识);
- geodist key member1 member2 [unit]:计算两个地理位置之间的距离;
- georadius key longitude latitude radius unit:查询指定坐标范围内的地理位置
- geohash key member:获取地理位置的 geohash 编码(用于压缩存储、近似查询)。
应用场景:附近的人、外卖商家定位、地图导航相关功能、地理位置范围筛选。
4. 流(Stream)
Stream 是 Redis 5.0 新增的高级数据结构,用于实现高性能的消息队列,支持消息持久化、分组消费、重复消费控制等功能,底层基于 listpack(列表压缩包)数据结构实现(Redis 5.0+ 替代 ziplist,更高效的内存存储方式),每个 Stream 由多个消息组成,每个消息包含唯一 ID 和键值对内容。
核心命令:
- xadd key ID field value...:向 Stream 中添加一条消息(ID 可自动生成或手动指定);
- xread [COUNT count] [BLOCK milliseconds] STREAMS key... ID...:读取 Stream 中的消息(支持阻塞读取);
- xgroup create key groupname ID:创建消费组;
- xreadgroup GROUP groupname consumername [COUNT count] [BLOCK milliseconds] STREAMS key... ID...:消费组读取消息;
- xack key groupname ID...:确认消息已消费(避免重复消费)。
应用场景:高可靠消息队列(如订单消息、通知消息)、分布式系统中的事件流处理、日志收集与分发。
三、核心总结(底层数据结构汇总)
Redis 数据结构的设计核心是“内存高效 + 操作高效”,不同结构的底层实现均围绕这一核心,以下是所有指定数据结构的底层数据结构汇总,方便快速查阅:
- String:底层为 简单动态字符串(SDS);
- Hash:底层为 ziplist(元素少、值小时)、hashtable(超过阈值时);
- List:底层为 ziplist(元素少、值小时)、quicklist(超过阈值时);
- Set:底层为 intset(整数、数量少时)、hashtable(其他情况);
- ZSet:底层为 跳表(skiplist)+ 哈希表;
- Bitmap:底层基于 String 数据结构;
- HyperLogLog:底层基于 String 数据结构;
- Geo:底层基于 ZSet 数据结构;
- Stream:底层为 listpack 数据结构。
选择合适的数据结构,本质是匹配其底层实现的优势与业务场景,从而最大化 Redis 的性能和内存利用率。
ps:如果这篇帖子对于还在找工作和找实习的你有所帮助,可以关注我,给本贴点赞、评论、收藏并订阅专栏;同时不要吝啬您的花花
Redis 作为高性能键值数据库,核心在于丰富的数据结构。本专栏聚焦String、Hash、List、Set、ZSet、Bitmap、HyperLogLog 等常用类型,从底层原理、使用场景到实战示例,清晰讲解每种结构的优缺点与最佳实践。帮你快速掌握如何用对数据结构,提升缓存、限流、排行榜、消息队列等业务场景的开发效率,写出更稳定、高效的 Redis 应用。

