动态字符串SDS
SDS是"simple dynamic string"的缩写。redis中所有场景中出现的字符串,基本都是由SDS来实现的
- 所有非数字的key。例如 setmsg"hello world" 中的key msg.
- 字符串数据类型的值。例如 set msg “hello world"中的msg的值"hello wolrd”
- 非字符串数据类型中的“字符串值”。例如 RPUSH fruits"apple"“banana”“cherry"中的"apple” “banana” “cherry”
SDS长这样:

free:还剩多少空间 len:字符串长度 buf:存放的字符数组
空间预分配
为减少修改字符串带来的内存重分配次数,sds采用了“一次管够”的策略:
- 若修改之后sds长度小于1MB,则多分配现有len长度的空间
- 若修改之后sds长度大于等于1MB,则扩充除了满足修改之后的长度外,额外多1MB空间

惰性空间释放
为避免缩短字符串时候的内存重分配操作,sds在数据减少时,并不立刻释放空间。

int
就是redis中存放的各种数字 包括以下这种,故意加引号“”的
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zPZ9jVzj-1613633258360)(https://ask8088-private-1251520898.cn-south.myqcloud.com/developer-images/article/7948575/4sokczupwd.png?q-sign-algorithm=sha1&q-ak=AKID2uZ1FGBdx1pNgjE3KK4YliPpzyjLZvug&q-sign-time=1613632061;1613639261&q-key-time=1613632061;1613639261&q-header-list=&q-url-param-list=&q-signature=a7c45192e02cd0bf9767ca227255fbceebcd66f6)] redis 键 的 层级结构_数据结构_04](https://s2.51cto.com/images/blog/202412/02132756_674d455c8983825589.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=/resize,m_fixed,w_1184)
双向链表
长这样:
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3VKgLTxv-1613633258362)(https://ask8088-private-1251520898.cn-south.myqcloud.com/developer-images/article/7948575/pwbtjl63yf.png?q-sign-algorithm=sha1&q-ak=AKID2uZ1FGBdx1pNgjE3KK4YliPpzyjLZvug&q-sign-time=1613632072;1613639272&q-key-time=1613632072;1613639272&q-header-list=&q-url-param-list=&q-signature=381a7df44ad25ea22e3c78c057c8abc2a1ecd6df)] redis 键 的 层级结构_链表_05](https://s2.51cto.com/images/blog/202412/02132756_674d455cb53df55973.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=/resize,m_fixed,w_1184)
分两部分,一部分是“统筹部分”:橘黄色,一部分是“具体实施方“:蓝色。
主体”统筹部分“:
- head指向具体双向链表的头
- tail指向具体双向链表的尾
- len双向链表的长度
具体"实施方":一目了然的双向链表结构,有前驱 pre有后继 next
由 list和 listNode两个数据结构构成。
小编这里也整理了一份Redis核心学习笔记,关注公众号:麒麟改bug获取。
ziplist
压缩列表。redis的列表键和哈希键的底层实现之一。此数据结构是为了节约内存而开发的。和各种语言的数组类似,它是由连续的内存块组成的,这样一来,由于内存是连续的,就减少了很多内存碎片和指针的内存占用,进而节约了内存。
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lVA2Cfse-1613633258363)(https://ask8088-private-1251520898.cn-south.myqcloud.com/developer-images/article/7948575/7vxffumuqk.png?q-sign-algorithm=sha1&q-ak=AKID2uZ1FGBdx1pNgjE3KK4YliPpzyjLZvug&q-sign-time=1613632104;1613639304&q-key-time=1613632104;1613639304&q-header-list=&q-url-param-list=&q-signature=fd29d8fdf850266dde3ed54f2072af274d61d049)] redis 键 的 层级结构_redis 键 的 层级结构_06](https://s2.51cto.com/images/blog/202412/02132756_674d455ce8ec875024.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=/resize,m_fixed,w_1184)
然后文中的 entry的结构是这样的:
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5dkpG8x2-1613633258364)(https://ask8088-private-1251520898.cn-south.myqcloud.com/developer-images/article/7948575/6xwjtokpcs.png?q-sign-algorithm=sha1&q-ak=AKID2uZ1FGBdx1pNgjE3KK4YliPpzyjLZvug&q-sign-time=1613632120;1613639320&q-key-time=1613632120;1613639320&q-header-list=&q-url-param-list=&q-signature=a42affa3cee79f4e15f86461aea27ae877c6f3f9)] redis 键 的 层级结构_字符串_07](https://s2.51cto.com/images/blog/202412/02132757_674d455d585a816556.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=/resize,m_fixed,w_1184)
元素的遍历
先找到列表尾部元素:

然后再根据ziplist节点元素中的 previous_entry_length属性,来逐个遍历:

连锁更新
再次看看 entry元素的结构,有一个 previous_entry_length字段,他的长度要么都是1个字节,要么都是5个字节:
- 前一节点的长度小于254字节,则 previous_entry_length长度为1字节
- 前一节点的长度大于254字节,则 previous_entry_length长度为5字节
假设现在存在一组压缩列表,长度都在250字节至253字节之间,突然新增一新节点 new, 长度大于等于254字节,会出现:

程序需要不断的对压缩列表进行空间重分配工作,直到结束。
除了增加操作,删除操作也有可能带来“连锁更新”。请看下图,ziplist中所有entry节点的长度都在250字节至253字节之间,big节点长度大于254字节,small节点小于254字节。

哈希表
哈希表略微有点复杂。哈希表的制作方法一般有两种,一种是:开放寻址法,一种是 拉链法。redis的哈希表的制作使用的是 拉链法。
整体结构如下图:
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uSpiphFW-1613633258369)(https://ask8088-private-1251520898.cn-south.myqcloud.com/developer-images/article/7948575/enh0jkw5hv.png?q-sign-algorithm=sha1&q-ak=AKID2uZ1FGBdx1pNgjE3KK4YliPpzyjLZvug&q-sign-time=1613632136;1613639336&q-key-time=1613632136;1613639336&q-header-list=&q-url-param-list=&q-signature=896760331fa27e745ae6a5da0f22529c6e5ef356)] redis 键 的 层级结构_redis 键 的 层级结构_12](https://s2.51cto.com/images/blog/202412/02132758_674d455e67cfa41269.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=/resize,m_fixed,w_1184)
也是分为两部分:左边橘黄色部分和右边蓝色部分,同样,也是”统筹“和”实施“的关系。具体哈希表的实现,都是在蓝色部分实现的。先来看看蓝色部分:
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rpgAkrhf-1613633258370)(https://ask8088-private-1251520898.cn-south.myqcloud.com/developer-images/article/7948575/zqi7eo7c8j.png?q-sign-algorithm=sha1&q-ak=AKID2uZ1FGBdx1pNgjE3KK4YliPpzyjLZvug&q-sign-time=1613632147;1613639347&q-key-time=1613632147;1613639347&q-header-list=&q-url-param-list=&q-signature=fead27a9225c326e3a882f5a5c5c8cdfc0753203)] redis 键 的 层级结构_链表_13](https://s2.51cto.com/images/blog/202412/02132758_674d455e9901851727.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=/resize,m_fixed,w_1184)
这也分为左右两边“统筹”和“实施”的两部分。
右边部分很容易理解:就是通常拉链表实现的哈希表的样式;数组就是bucket,一般不同的key首先会定位到不同的bucket,若key重复,就用链表把冲突的key串起来。
新建key的过程:

假如重复了:

rehash
再来看看哈希表总体图中左边橘黄色的“统筹”部分,其中有两个关键的属性:ht和 rehashidx。ht是一个数组,有且只有俩元素ht0和ht1;其中,ht0存放的是redis中使用的哈希表,而ht1和rehashidx和哈希表的 rehash有关。
rehash指的是重新计算键的哈希值和索引值,然后将键值对重排的过程。
加载因子(load factor)=ht0.used/ht0.size。
扩容和收缩标准
扩容:
- 没有执行BGSAVE和BGREWRITEAOF指令的情况下,哈希表的 加载因子大于等于1。
- 正在执行BGSAVE和BGREWRITEAOF指令的情况下,哈希表的 加载因子大于等于5。
收缩:
- 加载因子小于0.1时,程序自动开始对哈希表进行收缩操作。
扩容和收缩的数量
扩容:
- 第一个大于等于 ht0.used*2的 2^n(2的n次方幂)。
收缩:
- 第一个大于等于 ht0.used的 2^n(2的n次方幂)。
**(以下部分属于细节分析,可以跳过直接看扩容步骤)**对于收缩,我当时陷入了疑虑:收缩标准是 加载因子小于0.1的时候,也就是说假如哈希表中有4个元素的话,哈希表的长度只要大于40,就会进行收缩,假如有一个长度大于40,但是存在的元素为4即( ht0.used为4)的哈希表,进行收缩,那收缩后的值为多少?
我想了一下:按照前文所讲的内容,应该是4。但是,假如是4,存在和收缩后的长度相等,是不是又该扩容?翻开源码看看:
收缩具体函数:



由代码我们可以看到,假如收缩后长度为4,不仅不会收缩,甚至还会报错。()
我们回过头来再看看设定:题目可能成立吗?哈希表的扩容都是2倍增长的,最小是4, 4 ===》 8 ====》 16 =====》 32 ======》 64 ====》 128
也就是说:不存在长度为 40多的情况,只能是64。但是如果是64的话,64 X 0.1(收缩界限)= 6.4 ,也就是说在减少到6的时候,哈希表就会收缩,会缩小到多少呢?是8。此时,再继续减少到4,也不会再收缩了。所以,根本不存在一个长度大于40,但是存在的元素为4的哈希表的。
扩容步骤

收缩步骤

渐进式refresh
在"扩容步骤"和"收缩步骤" 两幅动图中每幅图的第四步骤“将ht0中的数据利用哈希函数重新计算,rehash到ht1”,并不是一步完成的,而是分成N多步,循序渐进的完成的。因为hash中有可能存放几千万甚至上亿个key,毕竟Redis中每个hash中可以存 2^32-1 键值对(40多亿),假如一次性将这些键值rehash的话,可能会导致服务器在一段时间内停止服务,毕竟哈希函数就得计算一阵子呢((#.#))。
哈希表的refresh是分多次、渐进式进行的。
渐进式refresh和下图中左边橘黄色的“统筹”部分中的 rehashidx密切相关:
- rehashidx 的数值就是现在rehash的元素位置
- rehashidx 等于 -1 的时候说明没有在进行refresh
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UC2DSnEu-1613633258379)(https://ask8088-private-1251520898.cn-south.myqcloud.com/developer-images/article/7948575/e5lgdjs9ud.png?q-sign-algorithm=sha1&q-ak=AKID2uZ1FGBdx1pNgjE3KK4YliPpzyjLZvug&q-sign-time=1613632172;1613639372&q-key-time=1613632172;1613639372&q-header-list=&q-url-param-list=&q-signature=3d95f748c58308a44ac3290e4405b2876ea308ee)] redis 键 的 层级结构_链表_21](https://s2.51cto.com/images/blog/202412/02132800_674d4560710b942812.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=/resize,m_fixed,w_1184)
甚至在进行期间,每次对哈希表的增删改查操作,除了正常执行之外,还会顺带将ht0哈希表相关键值对rehash到ht1。
以扩容步骤为例:

intset
整数集合是集合键的底层实现方式之一。
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AOLW25NY-1613633258380)(https://ask8088-private-1251520898.cn-south.myqcloud.com/developer-images/article/7948575/t9z277rqze.png?q-sign-algorithm=sha1&q-ak=AKID2uZ1FGBdx1pNgjE3KK4YliPpzyjLZvug&q-sign-time=1613632182;1613639382&q-key-time=1613632182;1613639382&q-header-list=&q-url-param-list=&q-signature=7ffd1cb54312fdebcbd5801debab169b1dc2b695)] redis 键 的 层级结构_redis 键 的 层级结构_23](https://s2.51cto.com/images/blog/202412/02132800_674d4560d3d2435843.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=/resize,m_fixed,w_1184)
跳表
跳表这种数据结构长这样:
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BOBlRUwB-1613633258381)(https://ask8088-private-1251520898.cn-south.myqcloud.com/developer-images/article/7948575/1k86wr8w78.png?q-sign-algorithm=sha1&q-ak=AKID2uZ1FGBdx1pNgjE3KK4YliPpzyjLZvug&q-sign-time=1613632198;1613639398&q-key-time=1613632198;1613639398&q-header-list=&q-url-param-list=&q-signature=16e08841d30eb3ac201de74ed89c5a2350e47f5c)] redis 键 的 层级结构_链表_24](https://s2.51cto.com/images/blog/202412/02132801_674d456111e1b36589.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=/resize,m_fixed,w_1184)
redis中把跳表抽象成如下所示:
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uBTBurlw-1613633258382)(https://ask8088-private-1251520898.cn-south.myqcloud.com/developer-images/article/7948575/lrwisn4wtw.png?q-sign-algorithm=sha1&q-ak=AKID2uZ1FGBdx1pNgjE3KK4YliPpzyjLZvug&q-sign-time=1613632218;1613639418&q-key-time=1613632218;1613639418&q-header-list=&q-url-param-list=&q-signature=46cbf7652d3d23675c17f107f6be72f003ae958f)] redis 键 的 层级结构_redis 键 的 层级结构_25](https://s2.51cto.com/images/blog/202412/02132801_674d45614c9c243467.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=/resize,m_fixed,w_1184)
看这个图,左边“统筹”,右边实现。统筹部分有以下几点说明:
- header: 跳表表头
- tail:跳表表尾
- level:层数最大的那个节点的层数
- length:跳表的长度
实现部分有以下几点说明:
- 表头:是链表的哨兵节点,不记录主体数据。
- 是个双向链表
- 分值是有顺序的
- o1、o2、o3是节点所保存的成员,是一个指针,可以指向一个SDS值。
- 层级高度最高是32。每次创建一个新的节点的时候,程序都会随机生成一个介于1和32之间的值作为level数组的大小,这个大小就是“高度”
小编这里也整理了一份Redis核心学习笔记,关注公众号:麒麟改bug获取。
redis五种数据结构的实现
redis对象
redis中并没有直接使用以上所说的各种数据结构来实现键值数据库,而是基于一种对象,对象底层再间接的引用上文所说的具体的数据结构。
结构如下图:
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vnJmxjxm-1613633258384)(https://ask8088-private-1251520898.cn-south.myqcloud.com/developer-images/article/7948575/k1agb0pgyw.png?q-sign-algorithm=sha1&q-ak=AKID2uZ1FGBdx1pNgjE3KK4YliPpzyjLZvug&q-sign-time=1613632233;1613639433&q-key-time=1613632233;1613639433&q-header-list=&q-url-param-list=&q-signature=117ca6539179d9046c7cef22a065aafb05f7946d)] redis 键 的 层级结构_java_26](https://s2.51cto.com/images/blog/202412/02132801_674d456184bc48662.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=/resize,m_fixed,w_1184)
字符串
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PzGP2Sb7-1613633258385)(https://ask8088-private-1251520898.cn-south.myqcloud.com/developer-images/article/7948575/0ygi5x28js.png?q-sign-algorithm=sha1&q-ak=AKID2uZ1FGBdx1pNgjE3KK4YliPpzyjLZvug&q-sign-time=1613632247;1613639447&q-key-time=1613632247;1613639447&q-header-list=&q-url-param-list=&q-signature=c0da326e0d236c42865342c6cf40f6b4a139e1d0)] redis 键 的 层级结构_字符串_27](https://s2.51cto.com/images/blog/202412/02132801_674d4561bc0e496637.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=/resize,m_fixed,w_1184)
其中:embstr和raw都是由SDS动态字符串构成的。唯一区别是:raw是分配内存的时候,redisobject和 sds 各分配一块内存,而embstr是redisobject和raw在一块儿内存中。
列表
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8fC5nYuw-1613633258386)(https://ask8088-private-1251520898.cn-south.myqcloud.com/developer-images/article/7948575/f35ahqi5bt.png?q-sign-algorithm=sha1&q-ak=AKID2uZ1FGBdx1pNgjE3KK4YliPpzyjLZvug&q-sign-time=1613632260;1613639460&q-key-time=1613632260;1613639460&q-header-list=&q-url-param-list=&q-signature=2a9adab60874564d25131985c2d153388aea4445)] redis 键 的 层级结构_redis 键 的 层级结构_28](https://s2.51cto.com/images/blog/202412/02132801_674d4561e46b273499.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=/resize,m_fixed,w_1184)
hash
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D9NGrKn4-1613633258387)(https://ask8088-private-1251520898.cn-south.myqcloud.com/developer-images/article/7948575/65ffnv08rc.png?q-sign-algorithm=sha1&q-ak=AKID2uZ1FGBdx1pNgjE3KK4YliPpzyjLZvug&q-sign-time=1613632272;1613639472&q-key-time=1613632272;1613639472&q-header-list=&q-url-param-list=&q-signature=9e03cf0ef37dbcc5ecd8d9683295c3596a2fe54f)] redis 键 的 层级结构_链表_29](https://s2.51cto.com/images/blog/202412/02132802_674d4562291c416303.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=/resize,m_fixed,w_1184)
set
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tF6kXfuS-1613633258388)(https://ask8088-private-1251520898.cn-south.myqcloud.com/developer-images/article/7948575/at2vwlc721.png?q-sign-algorithm=sha1&q-ak=AKID2uZ1FGBdx1pNgjE3KK4YliPpzyjLZvug&q-sign-time=1613632282;1613639482&q-key-time=1613632282;1613639482&q-header-list=&q-url-param-list=&q-signature=1150486ac6dda938ffbdf6e6a9fcf934dab80951)] redis 键 的 层级结构_数据结构_30](https://s2.51cto.com/images/blog/202412/02132802_674d45625f40d85385.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=/resize,m_fixed,w_1184)
zset
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HFWSRtvF-1613633258389)(https://ask8088-private-1251520898.cn-south.myqcloud.com/developer-images/article/7948575/6vjbc49xtw.png?q-sign-algorithm=sha1&q-ak=AKID2uZ1FGBdx1pNgjE3KK4YliPpzyjLZvug&q-sign-time=1613632294;1613639494&q-key-time=1613632294;1613639494&q-header-list=&q-url-param-list=&q-signature=fa1eefb4d8301660f377659b09ae1bd1943944f2)] redis 键 的 层级结构_链表_31](https://s2.51cto.com/images/blog/202412/02132802_674d4562b4faf54496.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=/resize,m_fixed,w_1184)
篇幅有限,其他内容就不在这里一一展示了,欢迎大家一起交流,喜欢文章记得关注我点赞哟,感谢支持!
















