string 字符串

字符串目标的完成有3种编码:INT、RAW和EMBSTR。

  • 当字符串目标保存的值为整数值,且在long类型表明规模内,那么运用INT编码。此刻,整数值会被保存到字符串目标中的ptr特点。
  • 当字符串目标保存的值为字符串值,且占用字节大于44字节,那么运用RAW编码。此刻,运用SDS来保存字符串值。
  • 当字符串目标保存的值为字符串值,且占用字节小于等于44字节,那么运用EMBSTR编码。此刻,也是运用SDS来保存字符串值。不同的是,关于EMBSTR编码来说,SDS和redisObject目标共用一块接连的内存空间。

为什么string决议运用RAW编码或EMBSTR编码,是以44字节为分界?

当字符串值最大占用字节为44字节,加上SDS其他部分占用的字节以及redisObject占用的字节,那么整个string目标最大占用字节为64字节。

redis的空间是以2的n次方来进行分配的,也就是说,这样可以削减内存碎片的产生。

EMBSTR编码和RAW编码都是运用SDS来保存字符串值,那么它们之间有什么区别?

  1. 关于RAW编码来说,创立字符串目标需求进行两次内存分配。而EMBSTR编码,只需求履行一次内存分配即可。
  2. 关于RAW编码来说,字符串目标的开释需求调用两次内存开释函数。而EMBSTR编码,只需求调用一次。
  3. EMBSTR编码经过将redisObject和SDS放到一块接连的内存空间,可以削减内存碎片的产生。

字符串编码之间的转换是怎么样的?

INT编码和EMBSTR编码在满意条件的情况下,都可以转换成RAW编码。

  • 当字符串目标进行修正操作使得保存的字符串值不再是整数值。那么redis就会将INT编码转换成RAW编码。
  • 关于EMBSTR编码来说,redis并没有为它编写对应的修正程序。也就是EMBSTR编码本质上来说是只读,那么当对选用EMBSTR编码的字符串目标,redis会先将EMBSTR编码转换成RAW编码,再履行修正指令。所以,只要EMBSTR编码的字符串目标进行了修正,无论如何都会变成RAW编码的字符串目标。

hash 哈希

哈希运用紧缩列表ziplist或字典dict来作为底层完成。

  • 若哈希运用ziplist作为底层完成。当需求刺进一个元素时,元素的键会被作为紧缩列表一个节点刺进队尾,然后再将元素的值作为紧缩列表一个节点刺进到队尾。
  • 若哈希运用字典dict作为底层完成。哈希目标的每一个键值对都运用一个字典键值对来进行保存。

哈希什么时候运用ziplist或dict来作为底层完成?

当哈希运用ziplist来作为底层完成时,对哈希的操作都需求遍历紧缩列表才干够找到对应的键值对。因而,ziplist只会在哈希内的元素不多而且元素占用字节数不大的情况下才会运用。不然,运用dict来作为底层完成。

list 列表

在redis 3.2曾经,list运用ziplist或linkedlist来作为底层完成。

在redis 3.2今后,list一致运用快速列表quicklist来作为底层完成。

那么在redis 3.2曾经,列表什么时候运用ziplist或linkedlist来作为底层完成?

当列表运用ziplist作为底层完成时,对元素的查找需求遍历整个列表,效率很低。因而,当元素占用字节数较小且元素不多的情况下才会运用ziplist。不然,运用linkedlist。

set 调集

set调集运用intset或dict来作为底层完成。

  • 当set运用intset来作为底层完成时,调集每个元素都存储到intset中。
  • 当set运用dict来作为底层完成时,调集的每个元素都作为字典的键来进行存储,对应的值设置为NULL。

set什么时候运用intset或dict来作为底层完成?

当set存储元素都是整数值且元素不多时,才运用intset来作为底层完成。不然运用dict。

inset内存储的元素都是有序的,为什么set要存储元素不多时才运用intset来作为底层完成?

intset内存储的元素都是有序的,这样对intset进行查找时,经过二分法也能很快找到需求的元素。即便,这比不上dict查找元素O(1)的时刻复杂度。

之所以,set要元素不多的时候才运用intset来作为底层完成的原因如下:

  1. 当intset存储的元素很多时,intset需求占用的内存也越大。而且intset是运用数组来存储这些元素的,那么请求一块较大的接连的内存空间的操作会比较耗时。
  2. intset运用了晋级机制,这样intset运用更加灵敏,而且可以节约内存。这样,若intset存储过多的元素,每个元素都需求放到新请求的空间的合适位置,这样的操作也会比较耗时。

zset 有序调集

有序调集运用ziplist 或 skiplist+dict来作为底层完成。

  • 若有序调集运用ziplist来作为底层完成。有序调集的每个元素运用两个紧挨着的紧缩列表节点来进行存储,第一个节点存储元素的成员,第二个节点存储元素的分值。而且紧缩列表中的元素按元素的分值从小到大进行排序,分值较小的元素被放到紧缩列表的表头方向。
  • 当有序调集运用skiplist和dict来作为底层完成时,skiplist中的每个节点都会存储有序调集的元素,字典键值对的键存储元素的成员,字典键值对的值存储元素的分值。这样,经过skiplist结构,有序调集可以有用的对调集进行规模性操作;经过dict结构,有序调集可以经过O(1)的时刻复杂度来完成对元素分值的查找操作。

为什么zset要运用skiplist+dict两个数据结构来完成有序调集?

尽管说zset可以独自运用skiplist或dict来完成有序调集,但相比于同时运用两个数据结构来完成有序调集,功能会有所下降。

当zset运用skiplist来独自完成有序调集时,尽管有序调集履行规模性操作的长处得以保存,可是查找元素分值的操作需求遍历元素才干完成。

当zset运用dict来独自完成有序调集时,尽管有序调集查找元素分值的时刻复杂度为O(1),可是履行规模性操作时需求将所有元素进行排序。

而经过同时运用两个数据结构来完成有序调集,可以兼具两个的长处,使得对有序调集的操作更加高效。

zset运用skiplist+dict两个数据结构来完成,那么不会很糟蹋内存吗?

不会的。这是因为关于共同的分值和成员,skiplist和dict经过指针来同享。经过这样的方式,相同的元素不需求存储两份,然后可以节约内存。

那么zset在什么时候决议运用ziplist或 skiplist+dict来完成?

当zset内的元素占用字节数不多且元素数量不多时,才运用ziplist来作为底层完成。不然运用skiplist+dict。