Redis - 图解redis五种常用数据结构底层实现

  Redis 使用键值对存储数据,其中的值(对象)包括 5 种类型,即字符串、列表、哈希、集合、有序集合,这是 Redis 对外提供的,实际上,在 Redis 内部每种类型可能有 2 种或更多的内部编码实现。

1、字符串(string)------用于一些常规计数:微博数,粉丝数等

其中:embstr和raw都是由SDS(简单动态字符串)构成的。

唯一区别是:raw是分配内存时,redisobject和sds各分配一块内存,而embstr是redisobject和raw在同一块内存中。

2、列表(list,查看底层实现)------用于存储对象:用户信息,商品信息等

压缩列表是在数据结构中元素较少时采取的数据结构,使用压缩列表节约了内存开支,但是降低了访问的速度,不过对于规模较小的对象,这样的损失可以忽略不记。

3、哈希(hash)------用于储存列表:微博的关注列表,粉丝列表等

4、集合(set,查看底层实现)------用于存储不重复的列表:共同关注、共同喜好、二度好友等

一个 value 为 null 的 hash 表,所以添加,删除,查找的复杂度都是0(1)。

5、有序集合(zset,查看底层实现)------用于带权重的集合:排行榜,弹幕消息(可理解为按消息维度的消息排行榜)、范围查询场景(需要过滤数据)等

简单说来跳跃表(查看实现原理)也是链表的一种,只不过它在链表的基础上增加了跳跃功能,正是这个跳跃的功能,使得在查找元素时,跳跃表能够提供O(logN)的时间复杂度。

跳跃表查询、插入、删除的时间复杂度均是O(logN)。

为什么使用跳表作为ZSet实现的索引,而不使用红黑树、B+树?(点击查看

总结:

压缩列表的由来:

压缩列表是经过特殊编码,由连续内存块组成的双向链表。它不存储指向上一个链表节点和指向下一个链表节点的指针,而是存储上一个节点长度和当前节点长度。专门为了提升内存使用效率设计的。所有的操作都是通过指针与解码出来的偏移量进行的。并且压缩列表的内存是连续分配的,遍历的速度很快。ziplist使用更加紧凑的结构实现多个元素的连续存储,所以在节省内存方面比hashtable更加优秀。

复制代码
# ziplist数据结构组成
<zlbytes> <zltail> <zllen> <entry> <entry> ... <entry> <zlend>

<zlbytes> :占用内存字节数
<zltail> :列表末尾偏移量
<zllen> :节点数
<entry> <entry> ... <entry> 
<zlend> :标记末端
复制代码

Redis 对象(List 对象、Hash 对象、Zset 对象)包含的元素数量较少,或者元素值不大的情况才会使用压缩列表作为底层数据结构。

设计思想是通过时间换空间,而时间的损耗又相对来说比小(小到几乎可以忽略)。

连锁更新

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

Redis 针对压缩列表在设计上的不足,在后来的版本中,新增设计了两种数据结构:quicklist(Redis 3.2 引入) 和 listpack(Redis 5.0 引入)。这两种数据结构的设计目标,就是尽可能地保持压缩列表节省内存的优势,同时解决压缩列表的「连锁更新」的问题。

posted @   李若盛开  阅读(508)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
点击右上角即可分享
微信分享提示