本文正在参与「金石计划」

1: String类型

1.1: String的三种编码类型

仔细看下面三幅图,都是设置一个key-value,可是发现三个值的编码都是不一样的,有int,embstr,raw,这也是redis中String的三种编码格局

花十分钟了解一下Redis五种数据类型低层数据结构
花十分钟了解一下Redis五种数据类型低层数据结构

花十分钟了解一下Redis五种数据类型低层数据结构
那么这三种编码格局有什么区别呢?

格局 区别
int 保存long型(长整型)的64位(8个字节)有符号整数,9223372036854775807,这是最大规模,只有整数会运用int,假如是浮点数,Redis内部其实先将浮点数转为字符串,然后仔保存
embstr 代表embstr格局的SDS(简略动态字符串), 保存长度小于44字节的字符串
raw 保存长度大于4医治4字节的字符串

1.2:为什么要运用三种编码格局??

是为了节约内存考虑,int需求的存储本钱最低,对于一般的整数类型直接运用int存储就行,能够节省内存空间

1.3:简略动态字符串-SDS

Redis是用c完成的,c是有字符串这种数据格局的,可是Redis并没有直接复用c的,而是自己完成了一个,也便是SDS

c言语 SDS
字符串长度处理 需求从头开始遍历,时刻复杂度为O(n) 记载了当时字符串的长度,时刻复杂度为O(1)
内存重新分配 分配内存空间超过后,会导致数组下标越界或者内存分配溢出,也便是不会动态的去分配内存 空间预分配:SDS修改后。len长度小于1M,那么将会额定分配与len相同长度的未运用空间,假如修改后长度大于1M,那么将再分配1M的运用空间。。。。。。。。。。。。。。。。。。。。。。慵懒空间开释:有空间分配对应的就会有空间开释,SDS缩短时分并不会收回多余的内存空间,而是运用free字段记载下来,假如后续有变更操作,直接运用free记载的空间,减少内存分配,由于收回分配内存空间都是有耗费的
二进制安全 二进制数据并不是规则的字符串格局,可能包括一些特殊的字符,比方’\O’等 ,c字符串中遇到\O就代表这个字符串完毕,那\O之后的数据就读不到了 根据len长度判断字符串的完毕,二进制安全的问题就解决了
保存数据类型 只能保存文本数据类型 保存文本还有二进制数据

2:Hash类型

很多人认为Redis的Hash便是运用Hash表来完成的,其实不是啊,看下图,其实Hash的底层数据结构是分状况来完成的,一个是紧缩列表-ziplist,一个是hashtable

花十分钟了解一下Redis五种数据类型低层数据结构

花十分钟了解一下Redis五种数据类型低层数据结构

hash-max-ziplist-entries:
运用紧缩列表保存时调集中的最大元素个数,超出了就会转换成hashtable,
比方你设置成了2,然后你执行 hset people name 1 age 2 address 3,此刻你设置了三个元素,分别是
name,age,address,现已大于2了,那么就不会运用ziplist了
hash-max-ziplist-value:运用紧缩列表保存时调集中单个元素的最大长度,超出了就会转换成hashtable
比方你设置成了2,这时分只需有一个元素的内容长度大于2,那么也会变成hastable存储
以上二个条件只需有一个不满足就都会运用hashtable来存储

2.1:紧缩列表-ziplist

  • 什么是ziplist: ziplist是一种紧凑的编码格局,整体来说是运用时刻换空间的做法,便是以部分读写功能为代价来交换极高的空间运用率。由于只会用于字段个数少并且字段内容小的场景。ziplist内存运用率高与其接连内存的特性是分不来的

  • ziplist数据结构

花十分钟了解一下Redis五种数据类型低层数据结构

特点 长度 作用
zlbytes 4字节 记载整个紧缩列表占用的内存字节数,在对紧缩列表进行内存重分配或者核算zlend的方位时运用
zltail 4字节 记载紧缩列表尾节点距离紧缩列表起始地址有多少字节,通过这个偏移量,程序无须遍历整个紧缩列表就能够定位表尾巴节点的地址
zllen 2字节 记载了紧缩列表包括的节点数量,当这个特点值大于65535时分,就需求遍历整个列表才干核算出了
entry 不定 存储我们的数据由三部分组成prevrawlen: 记载前一个节点所占内存的字节数,便利查找上一个元素地址。lensize, len:记载当时节点所占内存字节数,以及内容的存储类型,便利解析。content:保存了当时节点的值。
zlend 1字节 特殊值,用来标记紧缩列表的末端
  • ziplist的缺陷 – 连锁更新: 紧缩列表中每一个entry都记载了上一个entry所占的字节数,假如现在在链表中心刺进一个元素,那么之后的每一个元素的所占的字节数可能都会有改变,连锁更新在最坏状况下需求进行 N 次空间再分配,而每次空间再分配的最坏时刻复杂度为 O(N),因而连锁更新的整体时刻复杂度是 O(N^2)。

2.2:为什么要运用Hash要ziplist

在字段个数少并且字段内容比较少的状况下能够大大进步内存运用率,并且查询功率不会很慢,可是当节点数量很多的时分,Hash的时刻复杂度是O(1),对于ziplist的查询功率是O(n),功率就会大大降低,主要仍是在不同状况下在时刻空间上的取舍

3:List类型

在现在的版别中List底层是运用了quicklist数据结构,而quicklist是ziplist和linkedlist的结合体,详细如下:

花十分钟了解一下Redis五种数据类型低层数据结构

为什么要运用ziplist+linkedlist

很多人会问我直接运用其中一种不行吗?? 比方直接运用ziplist或者直接运用linkedlist,其实都行,可是会有问题

  • ziplist长处:运用接连内存的特点能够不保存prev和next指针,能够大大进步内存运用率

  • ziplist缺陷:需求运用很多的接连内存,并且ziplist是牺牲了读写功能的,还有连锁更新的问题

  • linkedlist长处:不需求对数据进行紧缩等操作,读写功能比ziplist好

  • linkedlist缺陷:需求维护prev和next指针,需求花更多的内存

List就结合了二者的长处,在每个linkedlist节点存储ziplist的,这样能够运用ziplist进步内存运用率。一起约束了ziplist的巨细,也避免了ziplist的缺陷

花十分钟了解一下Redis五种数据类型低层数据结构

  • list-max-ziplist-size:当取正值的时分,表明依照数据项个数来限定每个quicklist节点上的ziplist长度,比方,当这个参数装备成5的时分,表明每个quicklist节点的ziplist最多包括5个数据项,当取负值的时分,表明依照占用字节数来限定每个quicklist节点上的ziplist长度,这时,它只能取-1到-5这5个值
装备 含义
-5 每个quicklist节点上的ziplist巨细不能超过64kb
-4 每个quicklist节点上的ziplist巨细不能超过32kb
-3 每个quicklist节点上的ziplist巨细不能超过16kb
-2 每个quicklist节点上的ziplist巨细不能超过8kb,Redis默认值
-1 每个quicklist节点上的ziplist巨细不能超过4kb
  • list-compress-depth: 表明一个quicklist两头不被紧缩的节点个数,这儿的节点指的是quicklist双向链表的节点,而不是指ziplist里面数据项的个数
    装备 含义
    0 是个特殊值,表明都不紧缩,这是Redis默认值
    1 表明quicklist两头各有1个节点不紧缩,中心节点紧缩
    2 表明quicklist两头各有2个节点不紧缩,中心节点紧缩
    3 表明quicklist两头各有3个节点不紧缩,中心节点紧缩
    n 表明quicklist两头各有n个节点不紧缩,中心节点紧缩

4:Set类型

花十分钟了解一下Redis五种数据类型低层数据结构

intset

  • 当set存储的元素都是整型并且数量小于512的时分就会运用intset,这儿需求满足二个条件,整型小于512

花十分钟了解一下Redis五种数据类型低层数据结构

hashtable

  • 存储的不是整型或者元素个数大于512就运用hashtable来存储

花十分钟了解一下Redis五种数据类型低层数据结构

为什么要运用intset

intset底层运用接连内存来存储元素,所以对内存有一定的要求,由于运用的是数组来存储的,在存储元素的时分会进行排序,主要是在查找元素的时分是运用二分查找法来查找,所以需求确保数组的有序。hashtable底层由于要涉及到扩容,rehash,存储元素的时分还需求多保存一个null的value值。intset会更节省内存