Redis中的数据结构
字符串
SDS(simple dynamic string):redis自己构建的一种简单动态字符串,而没有直接使用C语言的字符串(在redis中C语言的字符串仅用在无需对字符串修改的地方,例如日志打印),SDS以空字符'\0'结尾,且不占用len里,会额外占用1字节空间,即使用长度为N+1的空间来表示长度为N的字符串
数据结构如下:
- int len:记录buf数组中已使用字节的数量,等于SDS所保存字符串的长度
- int free:记录buf数组中未使用字节的数量
- char buf[]:字节数组,用于保存字符串
SDS与C语言字符串的差异:
- SDS获取字符串长度的复杂度由O(N)降低到了O(1),有助于
strlen
命令的执行 - 避免缓冲区溢出:字符串拼接操作时可按长度分配空间,避免空间不足多次操作空间分配
- 减少修改字符串带来的内存重分配次数:数组长度不一定就是字符数量+1,可包含未使用空间即free属性记录,通过未使用空间,SDS实现了空间预分配和惰性空间释放两种优化策略:
- 空间预分配:在字符增加时不仅分配所需的空间,还会额外分配多余空间(如修改后长度小于1MB时,多余分配的空间free等于len,否则分配1MB);如果空间足够时将直接使用free空间而不再执行重新分配
- 惰性空间释放:释放用于优化SDS的字符串缩短操作,缩短字符串时不会释放多余的空间,为将来再次使用避免重分配
- 二进制安全:C语言字符串必须符合某种编码,且除末尾字符外其他不允许空字符,SDS则可存储空字符,其靠len判断是否结束而非空字符
- SDS兼容部分C语言字符函数:SDS遵循空字符结尾规则,可以使用部分C语言函数
链表
列表对象list的底层实现之一(另一实现的ziplist
)(元素长度存在大于等于64字节的或元素数量大于等于512个)
链表和链表节点listNode
的实现数据结构:双向链表
- listNode prev:
- listNode next;
- void value;可存储各种不同类型的值
list结构为链表提供了头尾指针以及链表长度len以及部分函数:
- listNode head;
- listNode tail;
- long len;
- dup:节点值复制函数
- free:节点值释放函数
- matcch:节点值对比函数
特性:双端,无环,带表头表尾指针,带链表长度,多态(配合3个函数可以存放各种不同类型的值)
字典
字典,符号表,关联数组,映射,map,保存键值对,底层使用哈希表实现
哈希表dictht
数据结构:
- dictEntry[] table;哈希表数组,每个dictEntry保存一个键值对
- long size;哈希表大小
- long sizemask;掩码,用于计算索引值,总是等于size-1
- long used;已有节点数量
哈希表节点dictEntry
数据结构:
- void key;键
- union:值,可以是一个指针,一个uint64_t的整数或者int64_t的整数
- void val;
- uint64_t u64;
- int64_t s64;
- dictEntry next;下个节点,形成链表,可以将多个哈希值相同的连接在一起,解决哈希冲突问题
字典dict
数据结构:
- dictType type;类型特定函数,为创建多态字典而设置的
- void privdata;私有数据
- dictht ht[2];哈希表,包含两个,字典只使用ht[0],ht[1]只会在对ht[0]进行rehash时使用
- int trehashidx;rehash索引,记录了rehash进度,当rehash不在进行时值为-1
hash冲突采用链地址法,通过hash表节点的next指针单链表解决hash冲突,为何不用其他数据结构,比如红黑树?
- 链表性能更好,红黑树对插入和删除性能不好
- 内存消耗更少
- 实现简单
- 单线程难以支持较高复杂度
- 冲突概率不高
hash表扩容与收缩条件,满足任意一个时执行:
- 服务器没有执行BGSAVE或BGREWRITEAOF并且hash表负载因子大于等于1
- 服务器正在执行BGSAVE或BGREWRITEAOF并且hash表负载因子大于等于5
- 负载因子小于0.1时开始执行收缩操作
负载因子:load_factor = ht[0].used / ht[0].size
渐进式rehash:避免数据量过于庞大时rehash导致的服务器暂停太久
- rehash过程中字典会同时使用ht[0]和ht[1]两个hash表
- rehash过程中字典的删除,查找,更新会在两个hash表上进行
- 新增键值对的操作一律保存到ht[1]中,ht[0]的数量只会减少不会增加
跳跃表
跳跃表由zskiplistNode和zskiplist两个数据结构定义,跳跃表仅用于有序集合zset
中
zskiplist
结构保存跳跃表节点信息,包括节点数量,头尾指针等,表头节点header仅记录了各个层(32,即最高层),不存储数据;zskiplistNode
结构用于表示跳跃表节点
- header:指向跳跃表的表头节点
- tail:指向跳跃表的表尾节点
- level:记录层数最大的那个节点的层数
- length:跳跃表长度,也就是节点数量
- 层:节点中用L1,L2,L3...标记各个层,每个层包括前进指针和跨度,1~32之间
- 后退指针:指向前一个节点,用于从表尾指针往前遍历
- score:分值,跳跃表按分值从小到大排列,分值相同时按成员对象的大小排序
- 成员对象obj:各个节点保存的对象
整数集合
整数集合intset
是集合set
的实现之一
可以保存int16_t,int32_t,int64_t的整数值,且保证不会出现重复元素
intset数据结构:
- encoding:编码方式int16_t,int32_t,int64_t,决定存储元素值的范围
- length:集合包含的元素数量,即数组长度
- contents[];保存元素的数组,从小到大排列,不含重复项
当向一个int16_t的数组中插入一个int64_t类型的数据时,数组中的其他所有值也会升级为int64_t,升级过程:
- 根据新的元素类型计算集合数组的空间大小,并为新元素分配空间
- 将底层所有元素换成新元素相同的类型,维持有序性不变
- 将新元素放到正确位置上(触发升级要么大于所有当前元素要么小于,所有新元素会放在0或者length-1的位置上)
升级的好处:
- 提升灵活性:通常不会将不同类型的元素放到同一数据结构中,升级操作可适应多种类型的场景
- 节约内存:按16,32,64三种类型存储,避免使用过大的类型浪费内存
整数集合不支持降级
压缩列表
ziplist是列表键和hash键的底层实现之一(保存元素长度均小于64字节且数量小于512个时)
一个压缩列表包含多个节点,每个节点保存一个字节数组和一个整数值:
属性 | 类型 | 长度 | 用途 |
---|---|---|---|
zlbytes | uint32_t | 4字节 | 记录整个压缩列表占用的字节数:在对压缩列表进行内存重分配或计算zlend的位置时使用 |
zltail | uint32_t | 4字节 | 记录压缩列表表尾节点距离列表起始地址有多少字节,通过这个偏移量可以计算出表尾节点地址 |
zllen | uint16_t | 2字节 | 记录列表节点数量,当值等于uint16_MAX时需要遍历整个列表才可以得到 |
entryX | 列表节点 | 不定 | 列表的各个节点,节点长度有节点保存的内容决定 |
zlend | uint8_t | 1字节 | 特殊值0xff,用于标记压缩列表的末端 |
节点entry的构成:
- previous:前一字节长度(1字节或5字节:长度小于2541字节,大于等于时5字节),可通过其计算出前一节点的起始地址,用于反向遍历
- entry_length
- encoding:记录了保存数据的类型及长度,可以是1字节,2字节或5字节,最高2位是字节数组编码,其他位为数组长度
- content:保存节点值,可以是字节数组或者整数,类型及长度由节点encoding决定
字节数组:长度可以分三类:encoding记录
- 长度小于等于63(2的6次方-1)字节的字节数组
- 长度小于等于16383(2的14次方-1)字节的字节数组
- 长度小于等于4294967295(2的32次方-1)字节的字节数组
整数值是以下6种长度之一:数组长度记录在encoding中
- 4位长,介于0~12之间的无符号整数
- 1字节长的有符号整数
- 3字节长的有符号整数
- int16_t类型整数
- int32_t类型整数
- int64_t类型整数
连锁更新:压缩列表中多个连续的长度在250~253字节之间的节点在头节点长度增加时,会导致previous属性值升级5字节而导致连锁更新反应
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
2020-06-17 锁