Redis系列-14.Redis经典五大类型源码及底层实现(二)(上)

简介: Redis系列-14.Redis经典五大类型源码及底层实现(二)

Redis经典五大类型源码及底层实现


Hash数据结构介绍


redis7


listpack+hashtable


hash-max-listpack-entries:使用压缩列表保存时哈希集合中的最大元素个数。


hash-max-listpack-value:使用压缩列表保存时哈希集合中单个元素的最大长度。


Hash类型键的字段个数 小于 hash-max-listpack-entries且每个字段名和字段值的长度 小于 hash-max-listpack-value 时,Redis才会使用OBJ_ENCODING_LISTPACK来存储该键,前述条件任意一个不满足则会转换为 OBJ_ENCODING_HT的编码方式

结论:


1.哈希对象保存的键值对数量小于512个

2.所有的键值对的键和值的字符串长度都小于等于64byte(一个英文字母一个字节)时用listpack,反之用hashtable

3.listpack升级到hashtable可以,反过来降级不可以


源码分析


实现:object.c

实现:listpack.c

lpNew 函数创建了一个空的 listpack,一开始分配的大小是 LP_HDR_SIZE 再加 1 个字节。LP_HDR_SIZE 宏定义是在 listpack.c 中,它默认是 6 个字节,其中 4 个字节是记录 listpack 的总字节数,2 个字节是记录 listpack 的元素数量。


此外,listpack 的最后一个字节是用来标识 listpack 的结束,其默认值是宏定义 LP_EOF。


和 ziplist 列表项的结束标记一样,LP_EOF 的值也是 255


实现:object.c


明明已经有ziplist了,为什么出来一个listpack紧凑列表呢?



ziplist的连锁更新问题


压缩列表新增某个元素或修改某个元素时,如果空间不不够,压缩列表占用的内存空间就需要重新分配。而当新插入的元素较大时,可能会导致后续元素的 prevlen 占用空间都发生变化,从而引起「连锁更新」问题,导致每个元素的空间都要重新分配,造成访问压缩列表性能的下降。


案例说明:压缩列表每个节点正因为需要保存前一个节点的长度字段,就会有连锁更新的隐患


第一步:现在假设一个压缩列表中有多个连续的、长度在 250~253 之间的节点,如下图:

因为这些节点长度值小于 254 字节,所以 prevlen 属性需要用 1 字节的空间来保存这个长度值,一切OK,O(∩_∩)O哈哈~


第二步:这时,如果将一个长度大于等于 254 字节的新节点加入到压缩列表的表头节点,即新节点将成为entry1的前置节点,如下图:

因为entry1节点的prevlen属性只有1个字节大小,无法保存新节点的长度,此时就需要对压缩列表的空间重分配操作并将entry1节点的prevlen 属性从原来的 1 字节大小扩展为 5 字节大小。


第三步:连续更新问题出现

entry1节点原本的长度在250~253之间,因为刚才的扩展空间,此时entry1节点的长度就大于等于254,因此原本entry2节点保存entry1节点的 prevlen属性也必须从1字节扩展至5字节大小。entry1节点影响entry2节点,entry2节点影响entry3节点…一直持续到结尾。这种在特殊情况下产生的连续多次空间扩展操作就叫做「连锁更新」


结论:listpack 是 Redis 设计用来取代掉 ziplist 的数据结构,它通过每个节点记录自己的长度且放在节点的尾部,来彻底解决掉了 ziplist 存在的连锁更新的问题


listpack结构



Total Bytes 为整个listpack的空间大小,占用4个字节,每个listpack最多占用4294967295Bytes。
num-elements 为listpack中的元素个数,即Entry的个数占用2个字节
element-1~element-N 为每个具体的元素
listpack-end-byte 为listpack结束标志,占用1个字节,内容为0xFF。

entry结构


  • 当前元素的编码类型
  • 元素数据
  • 以及编码类型和元素数据这两部分的长度


ziplist内存布局 VS listpack内存布局


和ziplist 列表项类似,listpack 列表项也包含了元数据信息和数据本身。不过,为了避免ziplist引起的连锁更新问题,listpack 中的每个列表项


不再像ziplist列表项那样保存其前一个列表项的长度。


List数据结构


Redis6


(1) ziplist压缩配置:list-compress-depth 0


表示一个quicklist两端不被压缩的节点个数。这里的节点是指quicklist双向链表的节点,而不是指ziplist里面的数据项个数


参数list-compress-depth的取值含义如下:


0: 是个特殊值,表示都不压缩。这是Redis的默认值。


1: 表示quicklist两端各有1个节点不压缩,中间的节点压缩。


2: 表示quicklist两端各有2个节点不压缩,中间的节点压缩。


3: 表示quicklist两端各有3个节点不压缩,中间的节点压缩。


依此类推…


(2) ziplist中entry配置:list-max-ziplist-size -2


当取正值的时候,表示按照数据项个数来限定每个quicklist节点上的ziplist长度。比如,当这个参数配置成5的时候,表示每个quicklist节点的ziplist最多包含5个数据项。当取负值的时候,表示按照占用字节数来限定每个quicklist节点上的ziplist长度。这时,它只能取-1到-5这五个值,


每个值含义如下:


-5: 每个quicklist节点上的ziplist大小不能超过64 Kb。(注:1kb => 1024 bytes)


-4: 每个quicklist节点上的ziplist大小不能超过32 Kb。


-3: 每个quicklist节点上的ziplist大小不能超过16 Kb。


-2: 每个quicklist节点上的ziplist大小不能超过8 Kb。(-2是Redis给出的默认值)


-1: 每个quicklist节点上的ziplist大小不能超过4 Kb。


Redis版本前List的一种编码格式


list用quicklist存储,quicklist存储了一个双向链表,每个节点都是一个ziplist

在Redis3.0之前,list采用的底层数据结构是ziplist压缩列表+linkedList双向链表


然后在高版本的Redis中底层数据结构是quicklist(替换了ziplist+linkedList),而quicklist也用到了ziplist


结论:quicklist就是「双向链表 + 压缩列表」组合,因为一个 quicklist 就是一个链表,而链表中的每个元素又是一个压缩列表

quicklist 实际上是 zipList 和 linkedList 的混合体,它将 linkedList按段切分,每一段使用 zipList 来紧凑存储,多个 zipList 之间使用双向指针串接起来。


源码分析


quicklist.h,head和tail指向双向列表的表头和表尾


quicklist结构



Redis系列-14.Redis经典五大类型源码及底层实现(二)(下):https://developer.aliyun.com/article/1414748

目录
相关文章
|
7月前
|
机器学习/深度学习 数据采集 人机交互
springboot+redis互联网医院智能导诊系统源码,基于医疗大模型、知识图谱、人机交互方式实现
智能导诊系统基于医疗大模型、知识图谱与人机交互技术,解决患者“知症不知病”“挂错号”等问题。通过多模态交互(语音、文字、图片等)收集病情信息,结合医学知识图谱和深度推理,实现精准的科室推荐和分级诊疗引导。系统支持基于规则模板和数据模型两种开发原理:前者依赖人工设定症状-科室规则,后者通过机器学习或深度学习分析问诊数据。其特点包括快速病情收集、智能病症关联推理、最佳就医推荐、分级导流以及与院内平台联动,提升患者就诊效率和服务体验。技术架构采用 SpringBoot+Redis+MyBatis Plus+MySQL+RocketMQ,确保高效稳定运行。
536 0
|
12月前
基于springboot+thymeleaf+Redis仿知乎网站问答项目源码
基于springboot+thymeleaf+Redis仿知乎网站问答项目源码
350 36
|
存储 消息中间件 NoSQL
Redis数据结构:List类型全面解析
Redis数据结构——List类型全面解析:存储多个有序的字符串,列表中每个字符串成为元素 Eelement,最多可以存储 2^32-1 个元素。可对列表两端插入(push)和弹出(pop)、获取指定范围的元素列表等,常见命令。 底层数据结构:3.2版本之前,底层采用**压缩链表ZipList**和**双向链表LinkedList**;3.2版本之后,底层数据结构为**快速链表QuickList** 列表是一种比较灵活的数据结构,可以充当栈、队列、阻塞队列,在实际开发中有很多应用场景。
|
消息中间件 存储 监控
redis 的List类型 实现 排行榜
【10月更文挑战第8天】
206 2
|
存储 NoSQL Redis
redis-set类型
【10月更文挑战第6天】
203 1
|
消息中间件 分布式计算 NoSQL
大数据-41 Redis 类型集合(2) bitmap位操作 geohash空间计算 stream持久化消息队列 Z阶曲线 Base32编码
大数据-41 Redis 类型集合(2) bitmap位操作 geohash空间计算 stream持久化消息队列 Z阶曲线 Base32编码
167 2
|
存储 NoSQL 关系型数据库
Redis的ZSet底层数据结构,ZSet类型全面解析
Redis的ZSet底层数据结构,ZSet类型全面解析;应用场景、底层结构、常用命令;压缩列表ZipList、跳表SkipList;B+树与跳表对比,MySQL为什么使用B+树;ZSet为什么用跳表,而不是B+树、红黑树、二叉树
|
消息中间件 NoSQL Kafka
大数据-116 - Flink DataStream Sink 原理、概念、常见Sink类型 配置与使用 附带案例1:消费Kafka写到Redis
大数据-116 - Flink DataStream Sink 原理、概念、常见Sink类型 配置与使用 附带案例1:消费Kafka写到Redis
924 0
|
8月前
|
缓存 NoSQL 关系型数据库
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
|
3月前
|
缓存 负载均衡 监控
135_负载均衡:Redis缓存 - 提高缓存命中率的配置与最佳实践
在现代大型语言模型(LLM)部署架构中,缓存系统扮演着至关重要的角色。随着LLM应用规模的不断扩大和用户需求的持续增长,如何构建高效、可靠的缓存架构成为系统性能优化的核心挑战。Redis作为业界领先的内存数据库,因其高性能、丰富的数据结构和灵活的配置选项,已成为LLM部署中首选的缓存解决方案。