当前位置: 首页 > news >正文

Redis数据结构

1.Set

Set(集合)底层数据结构:intset / hashtable。查询的时间复杂度为O(1)。

Redis 会根据数据大小和类型自动切换编码:

编码数据结构触发条件
intset整数集合(紧凑数组)所有元素都是整数,且数量 ≤ 512
hashtable哈希表元素包含字符串 / 数量超过限制

一旦从 intset 升级为 hashtable,就不会降级回来。

intset(整数集合)结构如下:使用二分查找判断是否存在、内存紧凑,适合小整数集合。📌 时间复杂度:O(log n)

typedef struct intset {uint32_t encoding;  // 编码格式:16/32/64位uint32_t length;    // 当前元素个数int8_t contents[];  // 存储整数的有序数组(节省内存)
} intset;

✅ hashtable(哈希表):实际是 Redis 的 dict 结构、key 是 set 元素,value 为 NULL。

查找时间复杂度:O(1)

// 简化版
dict {dictEntry **table;      // 哈希桶数组int size;               // 容量int used;               // 已用数量
}

当 set 中有非整数或数量太多时,自动升级为 hashtable


2.ZSet

ZSet(有序集合)底层数据结构为:ziplist/listpackskiplist + dict。查询的时间复杂度为O(log N)

底层编码方式(两种):

编码数据结构触发条件
ziplist(旧)或 listpack(Redis 7.0+)压缩列表元素少(默认 ≤ 128),且 member 和 score 较小
skiplist + dict跳表 + 字典超出上述限制时升级

💡 在新版 Redis 中,ziplist 已被更安全高效的 listpack 替代。

ZSet 从 listpack 转成 skiplist的时机,满足以下条件的任何一种:

  • 元素个数 > zset-max-listpack-entries(默认 128)。
  • 任一 member 或 score 的长度 > zset-max-listpack-value(默认 64 字节)。

核心结构之双索引设计:

typedef struct zset {dict *dict;        // member → score 映射(O(1) 查 score)zskiplist *zsl;    // score 排序链表(支持范围查询)
} zset;
结构作用
dict快速查找某个 member 的 score(用于 ZSCORE
zskiplist按 score 排序,支持 ZRANGE, ZRANK, ZREVRANGE

2.1.ziplist 的整体结构图解

+------------------+--------+-------------------+--------+------------------+
| zlbytes (4B)     | zltail (4B) | zllen (2B)   | ... entry ... | end (1B) |
+------------------+--------+-------------------+--------+------------------+

各字段含义:

字段大小说明
zlbytes4 字节整个 ziplist 占用的总字节数
zltail4 字节最后一个 entry 的偏移量(便于从尾部遍历)
zllen2 字节entry 的数量(注意:最大 65535,所以不能精确统计超过此数)
entry...变长实际存储的数据项(多个 entry 连续排列)
end1 字节特殊标记 0xFF,表示 ziplist 结束

所有数据都在一块连续内存中。

ziplist 中的每个元素称为一个 entry,其结构如下:

[prev_length] [encoding] [data]

1. prev_length:前一个 entry 的长度(用于反向遍历)

  • 如果前一个 entry 长度 < 254 字节 → 用 1 字节存储
  • 否则 → 用 5 字节(第一个字节是 0xFE,后面 4 字节是真实长度)

👉 这样可以从尾部向前遍历(类似双向链表)

2. encoding:当前 entry 的编码方式

用来标识后面 data 的类型和长度。

常见编码:

编码值(十六进制)含义
0xxx xxxx8/16/32 位整数(如 00 表示 int8)
1100 0000 ~ 1111 1110字符串,高位表示长度编码方式
1111 1111特殊用途,如 end 标记

3. data:实际存储的数据

  • 如果是字符串:直接存原始内容
  • 如果是整数:按 encoding 规则存二进制值(不转成字符串)

2.2.listpack

由于 ziplist 存在严重的 连锁更新问题,Redis 在 7.0 版本中引入了替代品:listpack。listpack 的改进:

特性ziplistlistpack
entry 大小记录前一个 entry当前 entry 头部
是否支持独立修改❌ 否(影响后续)✅ 是(不影响其他 entry)
内存重分配风险高(连锁更新)极低
实现复杂度更简单安全
是否已弃用✅ 是(Redis 7.0+)✅ 新默认编码

listpack 结构更清晰:

+--------+--------+--------+------------------+--------+
| total_bytes | count | ... entry ... | end (0xFF) |
+--------+--------+--------+------------------+--------+

每个 entry 自带长度信息,不再依赖前一个节点。


2.3.跳表+哈希表dict

在 Redis 的 zset(有序集合)中,当数据量超过压缩列表的阈值(元素数量过多或元素值过长)时,底层存储结构会转为 skiplist + dict 的组合结构。这种组合兼顾了有序性查询和快速的成员 - 分值映射,是 Redis 高效实现有序集合的核心设计。

skiplist + dict 的角色分工zset 的 skiplist + dict 结构中,两个组件分别承担不同职责:

  • 跳表(skiplist):负责维护元素的有序性,支持按分值(score)范围查询、排名计算等操作。
  • 字典(dict,哈希表):负责维护成员(value)到分值(score)的映射,支持通过成员快速查找对应的分值,以及判断成员是否存在。

两者存储的是同一批 zset 元素((value, score) 对),但组织方式不同,形成互补:跳表提供有序性和范围操作能力,字典提供 O (1) 级别的成员查询能力。

跳表(skiplist)的结构与作用。跳表是一种有序数据结构,通过在每个节点中维护多个指向其他节点的指针,实现快速查找、插入和删除。Redis 中的跳表(zskiplist)结构如下:

跳表(zskiplistNode)每个节点代表 zset 中的一个元素,包含:

  • value:成员值(字符串)。
  • score:分值(双精度浮点数),跳表按此值从小到大排序。
  • backward:指向当前节点的前一个节点(用于从后向前遍历)。
  • level:节点的层数(随机生成,决定该节点有多少个向前的指针)。
  • forward[]:每层的向前指针数组,forward[i] 指向当前节点在第 i 层的下一个节点。

跳表的整体结构包含:

  • header:头节点(不存储实际数据,作为查询的起点)。
  • tail:尾节点(指向最后一个元素)。
  • length:跳表中的节点总数(即 zset 的元素数量)。
  • level:当前跳表中最高节点的层数(方便查询时从最高层开始)。

跳表的作用:

  • 支持按 score 范围查询(如 ZRANGEBYSCORE)、按排名查询(如 ZRANGE)、计算元素排名(如 ZRANK)等有序操作,时间复杂度平均为 O (log N)。
  • 所有节点按 score 升序排列,若 score 相同,则按 value 的字典序排序。

字典(dict)的结构与作用:字典(哈希表)是 Redis 中用于快速映射的结构,zset 中的字典以 value 为键,score 为值,即 dict[value] = score

字典的作用:

  • 支持通过 value 快速查找对应的 score(如 ZSCORE 命令),时间复杂度为 O (1)。
  • 快速判断 value 是否存在于 zset 中(如 ZISMEMBER 命令)。
  • 插入或删除元素时,通过字典快速定位 value 对应的 score,避免遍历跳表。

skiplist + dict 的协同工作,zset 中的 skiplist 和 dict 存储的是同一批元素,两者保持强一致性:

  • 插入元素:当新增 (value, score) 时,既要在跳表中插入对应的节点(按 score 排序),也要在字典中添加 value → score 的映射。
  • 删除元素:删除时,需同时从跳表中移除节点和从字典中删除键值对。
  • 更新分值:若通过 ZADD 命令更新某个 value 的 score,会先通过字典找到旧 score,然后从跳表中删除旧节点,再插入新 score 对应的节点,并更新字典中的映射。

这种组合的优势:

  • 跳表的优势是有序性和范围操作,但通过 value 查找 score 时效率较低(需遍历)。
  • 字典的优势是 O (1) 级别的 value → score 映射,但无法维护有序性和支持范围查询。

两者结合后,zset 既能高效处理有序相关的操作(如排名、范围查询),又能快速通过成员定位分值,兼顾了性能和功能需求。

总结,Redis 的 zset 在使用 skiplist + dict 结构时:

  • 跳表负责维护元素的有序性,支持按分值 / 排名的范围操作。
  • 字典负责成员到分值的快速映射,支持 O (1) 级别的查询和存在性判断。
  • 两者存储相同的元素,操作时保持同步,共同实现了有序集合的高效功能。
http://www.dtcms.com/a/521748.html

相关文章:

  • ppt网站源码杭州网原创
  • wordpress怎么关注站点网页小程序
  • S8 链式栈
  • 中国免费建站网河间网站建
  • Datawhale25年10月组队学习:math for AI+Task4解析几何
  • 不干净的网站做性南京做网站企业
  • 龙虎榜——20251023
  • 矽塔 SA8206A 输入耐压36V 过流保护阈值1.4A 过压/过流保护芯片 SOT-23
  • seo如何提高网站排名做网站框架
  • 文章博客媒体网站模板怎样给自己的网站做防红连接
  • Flow Matching 时序任务:分布生成与多步动作序列的关联解析
  • 石家庄电商网站排名佛山市网站建设企业
  • 电子商务网站建设重点难点wordpress群组插件
  • 网站建设费税率网页传奇手游官网
  • 模板网站的缺陷wordpress建站的案例
  • 数据结构初阶:Java泛型
  • 推荐个好看的网站自己做网站花钱么
  • 潍坊设计网站wordpress任务论坛
  • 计网第二章——物理层
  • 河北网站推广网站购物车建设
  • 企业营销策略有哪几种莱芜网站优化招聘网
  • 39某健康网旋转验证码---Js逆向分析
  • 网站如何推广开发公司名称起名大全
  • 适合30岁短期培训班泉州seo排名
  • 欧洲稀土产业链
  • wordpress 去除tag自己的网站怎么优化
  • 网站建设多少钱合适临漳手机网站建设
  • 网络编程--TCP通信程序
  • GPIO口输入
  • 学校网站怎么做推广方案网站建设作业百度云资源