Redis 中 IntSet 底层数据结构

IntSet 底层数据结构

序言:

像字符串 SDS 只是保存了一个变量的值,但是像 Redis 中也是需要保存一些集合元素的,这里就介绍一下其中一种集合 IntSet,由于是 Set 所以也有 Set 的一些特性,不过也多加了一些特性:

● 唯一

● 长度可变

● 有序

● 可自动升级

数据结构

这里先来看一下 InSet 的底层结构:

contents数组 :用来保存元素的,用的就是 C 语言里面的数组,不过可以观察到这个数组的类型是 int8_t 就说明这个数组能够存的数字很小(-128~127)。难道这个 IntSet 的数据结构就只能存这些数字吗,其实并不是的,它到底存哪些数字主要由 InSet 的 encoding 字段来控制的,它本身算是充当一个数组的起始地址。
length :表示元素的个数。
encoding :编码方式,支持三种编码方式,这三种方式用来表示存储在数组中的数据能够表示多大,且每一个数字读取的方式和占用数组的大小也不一样。

这里以 16 位来举例:

比如存放一个 16 位能够表示的数字,那么单独放在 8 位的数组中是放不下的,所以在 contents 数组中会以两位来表示一个数字,那么读取的时候也是两位一起读然后转换成一个数字,所以说其实 contents 这个字段充当的是一个数组的起始地址,但是往里面存怎么样的数字和怎么读取都是由 IntSet 来控制的,可以利用多位来表示一个数组。

所以在 IntSet 中读取元素的方式也是有一个计算公式的。

bash 复制代码
startPtr + (sizeof(encoding) * index)

这个公式的具体含义就是,从数组起始地址开始 加上 读取元素索引 * 编码大小,就表示要读到的元素的位置。

由于每种编码的方式不同导致在一个数组中可能一个数占用的大小也不相同,所以需要索引 * 编码的大小。

并且如果读到元素之后也需要同样读取编码大小相同个数的位数才能组成想要的值。

由于要确保这个计算公式的准确性,所以 Inset 中要求存在数组中的编码要统一。

接下来举个例子:

存放5、10、20三个元素。

这里这三个数都在 16 位编码内能够存储,所以采用 16 编码,由于是16位编码,所以这三个元素每个元素都占用数组中的两位,也就是两个字节。

计算一下当前 IntSet 的大小。

● encoding:4 字节

● length:4 字节

● contents:2*3=6 字节

总共是 14 字节。

保存在 IntSet 中的数据也会保持一个有序的状态,为了就是能够使用二分查找法更快的找到元素。

IntSet 自动升级

不过之前说过要求存在 contents 中的数据要求编码必须统一为了方便计算要查找的数据的位置。但是如果有一个数要存到当前数组中,但是它的数对应的编码大于当前编码会怎么办。

这里就涉及到了 IntSet 的升级,会自动把数组中的所有数对应的编码升级。

举例:

例如现在还是存 5,10,20这三个数,对应的编码也是 16 位的。

此时如果向数组中添加一个 50000 超过 16 编码,需要使用 32 位编码,这时候 IntSet 会自动升级,会按照新的编码和元素个数进行数组的扩容。

这时每个元素都需要占用 4 个字节,确保编码的一致性,所以数组需要占用的内存就是(3+1) * 4 == 16 字节

计算完数组的大小之后,就需要将原先的数组中的数据扩大到相对应的编码大小之后,倒序的拷贝到扩容后的数组中。

这里为什么需要倒叙呢?

因为如果正序的话,假设这里是5先转移,那么 5 原先占用 2 个字节,然后扩容到四个字节然后在进行拷贝,在扩容到 4 个字节的时候就会把后面 10 这个数字给覆盖掉了,就导致数据的丢失,但是如果是倒叙,从 20 开始的话,20后面没有数字可以放行的扩容然后转移。所以需要倒倒序。

当转移完成之后:

这时候就可以把新要插入的数据放入到数组的尾部。

最后要修改头信息中的 encoding 和 length。

相关推荐
东阳马生架构32 分钟前
Redis应用—8.相关的缓存框架
redis
带多刺的玫瑰2 小时前
Leecode刷题C语言之k次乘运算后的数组②
c语言·数据结构·算法
口_天_光健2 小时前
两款轻量级数据库SQLite 和 TinyDB,简单!实用!
数据库·python·sqlite·非关系型数据库
notfindjob2 小时前
sqlite加密-QtCipherSqlitePlugin 下
数据库·算法·sqlite
凡人的AI工具箱2 小时前
每天40分玩转Django:Django部署
数据库·后端·python·算法·django
装不满的克莱因瓶2 小时前
【Redis经典面试题一】如何解决Redis和数据库一致性的问题?
数据库·redis·缓存·一致性·延迟双删·双写一致性
woshilys2 小时前
sql server msdb数据库备份恢复
数据库·sqlserver
殇淋狱陌2 小时前
第三章 列表(List)语法讲解
数据结构·python·学习·数据分析·list
play_big_knife2 小时前
鸿蒙项目云捐助第十六讲云捐助使用云数据库实现登录注册
数据库·华为云·harmonyos·鸿蒙·云开发·云数据库·鸿蒙开发
火鸟22 小时前
Java 初学者的第一个 SpringBoot3.4.0 登录系统
数据库·通用代码生成器·编程初学者·第一个系统·电音之王·springboot3.4.0·java初学者