Redis设计

文章目录

第一部分:编码

一、简单动态字符串(SDS)

简单字符串:Simple Dynamic String(SDS)

用处:

  • 保存数据库中的字符串值
  • 当作缓冲区:AOF缓冲区、客户端状态的输入缓冲区

1.1 SDS定义(结构体)

sds.h/sdshdr

struct sdshdr{
   
	// 记录buf数组中已使用的字节的数量
	// SDS保存的字符串的长度(不包括结尾的'\0')
	int len;
	
	// 记录buf数组中未使用字节的数量
	int free;
	
	// 字节数组,用户保存字符串
	char buf[];
}

示例:
在这里插入图片描述

  • free属性值为5:SDS为buf数组分配了5字节未使用空间
  • len属性值为5:SDS保存了5字节长的字符串

1.2 SDS 与C字符串的区别

1.2.1 常数获取字符串
  • 因为C字符串并不记录自身长度,每次必须遍历整个字符串进行计数,时间复杂度O(n)
  • SDS的len属性记录了SDS本身的长度,获取长度的复杂度为O(1)(设置和更新SDS长度是由SDS的API在执行时自动完成的)
1.2.2 杜绝缓冲区溢出

C字符串的添加操作:

假设程序里有两个内存紧邻的C字符串S1和S2,分别保存了“Redis”和“MongoDB”

在这里插入图片描述

如果程序猿决定执行strcat(s1,“ Cluster”),但是忘了在执行合并之前为S1分配足够的内存,那么在strcat函数执行后,s1的数据将溢出覆盖到S2所在的空间,导致S2的内容被意外的修改:

在这里插入图片描述

SDS的添加操作:

SDS在执行sdscat(将c字符串拼接到SDS保存的字符串后面)前会先判断剩余空间free够不够,如果不够会先扩展字符串的空间,再执行拼接操作

在这里插入图片描述

1.2.3 减少字符串修改带来的内存重分配次数

C字符串操作的缺陷:

​ 每次对C字符串执行添加(strcat)或截断操作(strim)都需要重新分配内存空间;在SDS中通过未使用空间(即free属性记录的值)实现空间预分配惰性空间释放两种优化策略。

  • 空间预分配:(SDS进行空间扩展的时候)

    • 如果扩展后SDS的长度(即len)小于1MB,那么程序分配和len属性同样大小的未使用空间free,此时len和free一样长,比如图2-10的free和len都为13字节。
    • 如果扩展后SDS的长度(即len)大于等于1MB,那么程序分配1MB的未使用空间free

    图2-10执行sdscat添加“ cluster”,重新分配内存后未使用空间free和len都为13字节,此时如果再执行sdscat添加操作,sdscat(s," aaa")则不用重新分配内存,因为free未使用空间有13字节满足“aaa”3个字节。

  • 惰性空间释放:(优化SDS字符串缩短操作)

    • SDS的API需要缩短SDS保存的字符串时,程序不需要立即重新分配内存来回收缩短后多出来的字节,而是使用free属性记录起来留着使用。

    比如SDS执行sdstrim(s,“XY”)移除SDS中所有的“X”和"Y",SDS并没有立即回收多余的8字节,如果将来要执行添加操作,则可以直接添加,减少内存重分配次数。

    在这里插入图片描述

1.2.4 二进制安全

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XxShgDCu-1593449631672)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200628234425808.png)]

  • C字符串通过’\0‘来判断字符串结尾,对于含有’\0’的特殊数据格式,只能读出Redis
  • SDS API使用len属性值判断字符串是否结束,不会过滤掉中间的’\0’,数据在输入时是什么样,被读取时就是什么样
1.2.5 兼容部分C字符串函数

SDS遵循C字符串以’\0‘结尾的惯例,分配空间时总会多分配一个字节的空间保存’\0‘,可以重用部分<String.h>库定义的函数

1.2.6 区别总结

在这里插入图片描述

1.3 SDS常用API

在这里插入图片描述

总结

  • Redis只会使用C字符串作为字面量,在大多数情况下,Redis使用SDS(SimpleDynamic String,简单动态字符串)作为字符串表示。

  • 比起C字符串,SDS具有以下优点:

    1. 常数复杂度获取字符串长度。
    2. 杜绝缓冲区溢出。
    3. 减少修改字符串长度时所需的内存重分配次数。
    4. 二进制安全。
    5. 兼容部分C字符串函数。

二、链表(双向链表)

用途:列表键的实现之一

2.1 链表的结构

链表节点的结构体
在这里插入图片描述

链表的结构体(双向链表)

在这里插入图片描述
在这里插入图片描述在这里插入图片描述

2.2 链表的特点

  1. 双端:pre和next指针
  2. 带表头指针和表尾指针
  3. 无环:表头节点的pre和表尾的next都指向null
  4. 带链表长度计数器:list结构体里的len属性记录了链表的长度

2.3 链表和链表节点的API

在这里插入图片描述
在这里插入图片描述

总结

  • 链表被广泛用于实现Redis的各种功能,比如列表键、发布与订阅、慢查询、监视器等。
  • 每个链表节点由一个listNode结构来表示,每个节点都有一个指向前置节点和后置节点的指针,所以Redis的链表实现是双端链表。
  • 每个链表使用一个list结构来表示,这个结构带有表头节点指针、表尾节点指针,以及链表长度等信息。
  • 因为链表表头节点的前置节点和表尾节点的后置节点都指向NULL,所以Redis的链表实现是无环链表。
  • 通过为链表设置不同的类型特定函数,Redis的链表可以用于保存各种不同类型的值。

三、字典

用途:

  • Redis数据库:对数据库的增删改查都是建立在对字典的操作上
  • 哈希键的底层实现之一

3.1 字典的结构体

3.1.1 哈希表节点

dict.h/dictEntry结构定义(类似HashMap里的Entry
在这里插入图片描述

  • key:保存键值对中的键
  • v:保存键值对的值,可以是一个指针,或者uint64_t整数或者int64_t整数
  • next:指向下一个哈希表节点的指针
3.1.2 哈希表

dict.h/dictht结构定义

  • table:哈希表节点数组,数组的每个元素都是指向dictEntry结构体的指针(类似HashMap的table)
  • size:哈希表的大小
  • used:哈希表已有节点(键值对)的数量
3.1.3 字典

dict.h/dict

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iNArPonP-1593450258328)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200629002542757.png)]

  • ht是两个哈希表的数组,字典只使用ht[0],rehash时使用ht[1]
  • type属性如图[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LCo3kvyd-1593450258336)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200629003049556.png)]
3.1.4 字典结构图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FilGl1Nn-1593450258349)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200629002804231.png)]

3.2 Hash算法

  1. 先利用字典的hash函数根据key计算出hash值:使用dict->type->hashFunction(key)
  2. 根据hash值计算索引值:将hash值与sizemask进行与运算(即hash & (size-1),与hashMap原理相同

3.3 Hash冲突解决

Redis使用链地址法解决Hash冲突,与HashMap相同,但是Redis为了考虑速度,将冲突的键值对插入到链表的表头位置复杂度为O(1)

3.4 rehash

rehash:执行扩展或伸缩过程中,给ht[1]分配空间,将ht[0]的键值对重新计算哈希值和索引值,放到ht[1]的指定位置

rehash过程

  1. ht[1]分配的空间大小取决于当前键值对数量(即ht[0].used)
    • 如果执行扩展操作:ht[1]大小为第一个大于等于2*ht[0].used的2^n(2的n次幂),例如当前为12,12*2=24,最接近24的2的幂次方是32
    • 如果执行收缩操作:ht[1]大小为第一个大于等于ht[0].used的2^n(2的n次幂)
  2. 将保存在ht[0]的所有键值对rehash到ht[1]上面
  3. ht[0]的所有键值对都rehash到ht[1]后,释放ht[0],将ht[1]设置为ht[0],并在ht[1]创建一个空的hash表(table为null),为下一次rehash使用。

负载因子:ht[0].used / ht[0].size

满足以下条件时,会自动对hash表执行扩展操作

  1. 服务器目前没有执行BGSAVE或BGREWRITEAOF命令,并且哈希表的负载因子大于等于1

  2. 服务器目前正在执行BGSAVE或BGREWRITEAOF命令,并且哈希表的负载因子大于等于5

    注意:BGSAVE或BGREWRITEAOF命令执行需要创建子进程,服务器会提高扩展所需的负载因子来避免执行扩展操作,避免不必要的内存写入操作,最大限度节约内存。

满足以下条件时,会自动对hash表执行收缩操作

  1. 哈希表的负载因子小于0.1

3.5 渐进式rehash

因为哈希表的键值对可能成千上万,如果一次性rehash到新的哈希表,由于庞大的计算量导致redis服务器在一段时间内停止服务,造成性能影响,所以redis是渐进式、分多次的将ht[0]里的键值对慢慢的rehash到ht[1]

  1. 为ht[1]分配空间,字典同时拥有ht[0]和ht[1]两个哈希表
  2. 字典中位置变量rehashindex,值设为0表示正在rehash
  3. rehash期间,每次对字典的添加、删除、查找、更新时,除了执行指定操作外,顺便将ht[0]的键值对rehash到ht[1]上,每次rehash完成之后,rehashindex值加一。
    • 删除、查找、更新操作在ht[0]和ht[1]两个哈希表上进行,比如查找会现在ht[0]查找,如果找到除了返回键值对外,由于查找时计算了hash值可以顺便映射到ht[1]上,没找到再去ht[1]查找
    • 增加操作只会在ht[1]执行,不会在ht[0],这样保证了ht[0]的键值对只增不减,随着rehash不断执行最终变成空表
  4. 随着操作不断进行,ht[0]的所有键值对都会rehash到ht[1]上,此时将rehashindex设为-1表示rehash完成

3.6 字典API

在这里插入图片描述

总结

  • 字典被广泛用于实现Redis的各种功能,其中包括数据库和哈希键。
  • Redis中的字典使用哈希表作为底层实现,每个字典带有两个哈希表,一个平时使用,另一个仅在进行rehash时使用。
  • 当字典被用作数据库的底层实现,或者哈希键的底层实现时,Redis使用MurmurHash2算法来计算键的哈希值。
  • 哈希表使用链地址法来解决键冲突,被分配到同一个索引上的多个键值对会连接成一个单向链表。
  • 在对哈希表进行扩展或者收缩操作时,程序需要将现有哈希表包含的所有键值对rehash到新哈希表里面,并且这个rehash过程并不是一次性地完成的,而是渐进式地完成的。

四、跳跃表

用途:有序集合键的底层实现之一。

支持平均复杂度O(logN),最坏复杂度O(N)的节点查找

4.1跳跃表的结构体

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u32NcDFj-1593450454712)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200629112310227.png)]

跳跃表节点的结构体(redis.h/zskiplistNode)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pgPV0KkR-1593450454714)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200629112133144.png)]

  • 层(level):level数组每个元素包含一个指向其他节点的指针forward和跨度span,可以加快访问节点的速度。(每一层类似在链表之上再建立的多层索引),每次创建节点的时候都随机生成一个[1,32]之间的数作为level数组的大小,这个大小就是层的高度
  • 前进指针(forward):用于从表头向表尾访问节点
  • 跨度:记录两个节点的距离,指向NULL的前进指针的跨度都为0
  • 后退指针(backword):用于从表尾向表头访问节点,每个节点只有一个后退指针,只能退前一个节点
  • 分值(score):double类型的浮点数,跳跃表的节点按照分值从小到大排序
  • 成员对象(obj):是一个指针,指向一个字符串对象,字符串对象保存着一个SDS值

注意:同一个跳跃表中,各个节点保存的节点对象必须是唯一的,分值可以相同,分值相同的按照字典序大小进行排序

跳跃表的结构体

typedef struct zskiplist{
	// 表头节点和表尾节点
	struct zskiplistNode *header,*tail;
	
	// 标中节点的数量
	unsigned long length;
	
	// 表中层数最大的节点的层数
	int level;
	
}zskiplist;
  • header:指向表头指针
  • tail:指向表尾指针,定位表尾的复杂度为O(1)
  • length:记录节点的数量,获取长度的复杂度为O(1)
  • level:O(1)复杂度获取跳跃表中层最高的节点的层数(注意:表头节点的高度不计算在内

4.2 跳跃表API

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1ZNaOWNV-1593450454727)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200629115051144.png)]

总结

  • 跳跃表是有序集合的底层实现之一。
  • Redis的跳跃表实现由zskiplist和zskiplistNode两个结构组成,其中zskiplist用于保存跳跃表信息(比如表头节点、表尾节点、长度),而zskiplistNode则用于表示跳跃表节点。
  • 每个跳跃表节点的层高都是1至32之间的随机数。
  • 在同一个跳跃表中,多个节点可以包含相同的分值,但每个节点的成员对象必须是唯一的。
  • 跳跃表中的节点按照分值大小进行排序,当分值相同时,节点按照成员对象的大小进行排序。

五、整数集合

用途:集合键的底层实现之一

5.1 整数集合结构体

intset.h/intset

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lmDw4hi4-1593450454730)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200629131819531.png)]

  • contents:该数组的每个元素都是整数集合的元素,按值大小从小到大排序,并且不包含重复项
  • length:记录了整数集合包含的元素数量,也是contents数组的长度
  • encoding:决定contents数组的类型,contents的定义为int8_t,但实际取决于encoding的值:
    • int16_t:最小值-32768,最大值32767
    • int32_t:最小值-2147483648,最大值2^32-1
    • int64_t:最小值-264,最大值244-1

5.2 升级

升级定义:当将一个新元素添加到整数集合里,并且新元素的类型比原来整数集合里的类型都要长时,整数集合需要进行升级,然后才能将新元素添加到整数集合里。

升级过程:

  1. ​ 根据新元素的类型,扩展整数集合数组contents的大小,并为新元素分配空间
  2. ​ 将底层数组的所有元素类型转换为与新元素相同的类型,并将转换的元素放到正确的位上保持有序性。
  3. ​ 将新元素添加到底层数组里面:
    • 如果新元素比数组的所有元素都小(负数),则插在数组第0位
    • 如果新元素比数组的所有元素都大(整数),则插在数组第length-1位

升级示例:

在这里插入图片描述

将int32_t类型的65535添加进去,需要重新分配空间。(32x4=128,还需要128-16x3 =80位)

在这里插入图片描述

升级的好处:

  1. 提高灵活性:
    • C语言存放int16、int32、int64分别用不同类型的数组保存不同的元素类型
    • 整数集合通过自动升级数组来适应元素,所以可以随意的将int16、int32、int64添加到集合中,而不必担心出现类型错误
  2. 节约内存:要让一个数组保存int16、int32、int64三种类型的值,最简单就是申明为int64类型的数组,可以全部兼容,但是这样如果都是int16或int32类型的元素,将会浪费内存空间,整数集合的升级能同时保存三种不同类型的元素,只有在需要的时候才会对类型升级,确保节约内存

5.3 降级

整数集合不支持降级操作,如果将集合从int16升级到int32后,将int32类型的元素删光只剩int16,整数集合的编码仍然会维持int32

5.4 整数集合API

在这里插入图片描述

总结

  • 整数集合是集合键的底层实现之一。
  • 整数集合的底层实现为数组,这个数组以有序、无重复的方式保存集合元素,在有需要时,程序会根据新添加元素的类型,改变这个数组的类型。
  • 升级操作为整数集合带来了操作上的灵活性,并且尽可能地节约了内存。
  • 整数集合只支持升级操作,不支持降级操作。

六、压缩列表

用途:列表键哈希键的底层实现之一

特点:从表尾往表头遍历

6.1 压缩列表结构组成

在这里插入图片描述

示例:

在这里插入图片描述

压缩列表节点的组成:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M6Bot4pd-1593450454742)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200629140950128.png)]

  • previous_entry_length:以字节为单位,记录了压缩列表前一个节点的长度

    • 如果前一字节的长度小于254字节,则previous_entry_length属性的长度为1字节(8位可以表示254以内的数字)
    • 如果前一字节的长度大于等于254字节,则则previous_entry_length属性的长度为5字节,其中第一个字节会被设置为0xFE(十进制254),剩余四个字节表示前一个节点的长度
  • encoding:纪律节点的content属性保留的数据类型及长度:

    • 1字节、2字节、5字节:值的最高位为00、01、10的是字节数组的编码,表示content保存的是字节数组,数组的长度由编码去掉最高的两位之后的二进制表示

    • 1字节,最高位11开头的是整数编码,整数值的类型由编码去掉最高两位后的二进制表示

  • content:负责保存节点的值,可以是一个字符数组或者一个整数值,类型和长度由encoding确定

    字符数组要求:

    • ​ 长度小于等于(2^6-1)字节的字符数组
    • ​ 长度小于等于(2^14-1)字节的字符数组
    • ​ 长度小于等于(2^32-1)字节的字符数组

    整数值要求:

    • 4位长,介于0-12之间的无符号整数
    • 1字节长的有符号整数
    • 3字节长的有符号整数
    • int16_t类型的整数
    • int32_t类型的整数
    • int64_t类型的整数

示例1:
在这里插入图片描述

  • 编码最高的两位00表示保存的是一个字节的字符数组
  • 后六位001011表示字节数组的长度是11
  • content属性的值为“hello world”

示例2:
在这里插入图片描述

  • 编码的11表示保存的是整数
  • 后6位为0表示整数是int16类型
  • content属性的10086是节点的值

6.2 连锁更新

特例:有一个压缩列表,所有节点(即e1到eN节点)的大小都是250-253字节的,这是记录前一个节点的previous_entry_length的大小只要一个字节

在以上例子发生一下情况会产生链所更新:

  1. 将一个大于254字节的节点插入到表头:新节点的大小大于254字节,则e1需要用一个5字节的previous_entry_length记录,由于e1原来的长度是250-253字节,这时候e1的previous_entry_length改为5字节,增加了4字节,将会大于等于254字节,导致e2的previous_entry_length也需要更新,产生连锁反应。
  2. 删除节点:e1前面有一个small节点小于254字节,small前面有个big节点,大于254字节。这时e1的previous_entry_length记录的是small节点的大小只需一个字节,small节点删除后,e1的previous_entry_length记录的是big节点的大小超过254字节,需要换成五字节的previous_entry_length,导致连锁更新。

注意:

  • 连锁更新最坏情况需要对压缩列表进行N次空间重分配,每次空间分配的最坏复杂度为O(N),所以连锁更新的最坏复杂度为O(N^2)
  • 真正造成连锁更新的可能性很低
    • 首先,压缩列表里恰好you好多个长度介于250-253字节之间的节点才会发生连锁更新,但是这种情况不多见
    • 其次,即使出现连锁更新,只要被更新的节点数量不多,救不会对性能造成影响。

6.3 压缩列表API

在这里插入图片描述

总结

  • 压缩列表是一种为节约内存而开发的顺序型数据结构。
  • 压缩列表被用作列表键和哈希键的底层实现之一。
  • 压缩列表可以包含多个节点,每个节点可以保存一个字节数组或者整数值。
  • 添加新节点到压缩列表,或者从压缩列表中删除节点,可能会引发连锁更新操作,但这种操作出现的几率并不高。

第二部分:对象实现

Redis对象

Redis对象的组成

Redis数据库创建一个键值对时,会创建两个对象:一个对象用作键值对的键(字符串对象),另一个对象用作键值对的值

redisObject对象:

typedef struct redisObject{
	// 类型
	unsigned type:4;
	
	// 编码
	unsigned encoding:4;
	
	// 指向底层实现数据结构的指针
	void *ptr;
	
	//引用计数(内存回收使用)
	int refcount;
	
	// 记录对象最后一次被命令程序访问的时间
	unsigned lru:22;
	
	// ...
	
} robj;

  • 类型:表示字符串、列表、哈希、集合、有序集合物种类型。称呼“字符串键”表示键对应的值是字符串对象,“列表键”表示键对应的值对象是列表对象。TYPE命令返回的是值对应的类型
  • ptr:根据encoding属性,ptr指向对象的底层数据结构实现(即SDS、字典、双端链表、跳跃表、整数集合、压缩列表等),可以使用OBJECT ENCODING命令查看对象的编码

在这里插入图片描述

五种数据类型对应的编码

一、字符串对象

字符串对象是五种类型对象中会被其他四种类型对象嵌套使用的

1.1 int编码

如果字符串对象保存的是整数值,并且这个整数值可以用long类型来表示,并将字符串对象的编码设置为int

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Tk4oUmRo-1593450863698)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200629152536711.png)]

1.2 raw编码(SDS)

要求:

  • 对象保存的是一个字符串值
  • 字符串值的长度大于32字节

结果:

  • 使用简单动态字符串SDS保存字符串值
  • 编码设置为raw

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iTlELSPH-1593450863700)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200629153002581.png)]

1.3 embstr编码(SDS)

embstr编码是一种专门保存短字符串的优化编码方式

要求:

  • 对象保存的是一个字符串值
  • 字符串值的长度小于等于32字节

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LHRhZEQp-1593450863711)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200629153922457.png)]

embstr与raw编码的区别:

  • raw编码通过两次内存分配分别创建redisObject和sdshdr,而embstr则通过一次内存分配来分配一块连续的内存空间给redisObject和sdshdr

embstr的好处:

  • embstr编码将创建字符串对象的内存分配次数从raw编码的两次降为1次
  • 释放embstr编码的字符串对象只要1次,raw编码需要两次
  • embstr编码的所有字符串对象的数据都在连续一块内存里,比raw编码更好的利用缓存带来的优势

注意:

  • long double类型的浮点数在redis中是作为字符串保存的
  • 取出是会先将字符串转化为浮点数,运算后再转化为字符串保存

1.4 编码的转换

  • 当对int编码的字符串执行append操作,将会把对象的编码转化为raw编码,再执行append操作
  • 对embstr编码的对象执行任何修改操作,程序会将对象的embstr编码转化为raw编码,再执行修改命令。
    • 因为redis没有为embstr编码的字符串编写任何相应的修改程序,所以embstr编码的字符串对象实际上是只读的,执行

1.5 字符串命令的实现

二、列表对象

2.1 ziplist编码(底层压缩列表)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6c73c5A5-1593450863712)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200629155810611.png)]

2.2 linkedlist编码(底层双端列表)

每个双端链表节点都保存了一个字符串对象,字符串对象里保存了列表元素(即链表节点的value嵌套指向了一个SDS,里面的字符串数组保存了节点的值)

字符串对象是五种类型对象中会被其他四种类型对象嵌套使用的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vRZiEm8A-1593450863713)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200629161527634.png)]

2.3 编码转换

使用ziplist编码:

  • 列表对象保存的所有字符串元素的长度小于64字节
  • 列表对象保存的元素数量小于512个

(上限值可以修改,参考list-max-ziplist-value和list-max-ziplist-entries两个选项说明)

不能满足以上两个条件将使用linkedlist编码

2.4 列表命令

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S2i813v1-1593450863716)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200629161048444.png)]

三、哈希对象

3.1 ziplist编码(底层压缩列表)

  • 先把键对象添加到链表末尾,再将值对象添加到列表末尾:键和值对象所在的节点是紧挨的,键节点在前,值节点在后
  • 先添加的键值对会在偏表头方向,后添加的键值对会在偏表尾的方向

在这里插入图片描述

3.2 hashtable编码(底层字典实现)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0BuDIiLR-1593450863720)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200629162008790.png)]

3.3 编码转换

使用ziplist编码(压缩列表)

  • 哈希对象所保存的所有键值对的键和值的字符串长度都小于64字节
  • 哈希对象所保存的键值对数量小于512个

(上限值可以修改,参考hash-max-ziplist-value和hash-max-ziplist-entries两个选项说明)

以上任意一个条件不被满足时,就换编码转换,将压缩列表里的所有键值对转移并保存到字典里

3.4 哈希命令的实现

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1zC82cIS-1593450863722)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200629162515655.png)]

四、集合对象

4.1 intset编码(底层整数集合)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yOrO7NtW-1593450863724)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200629162801501.png)]

4.2 hashtable编码(底层字典)

注意:字典的值全部设为NULL

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JzR5U1At-1593450863726)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200629162902017.png)]

4.3 编码转换

使用intset编码:

  • 集合对象保存的所有元素都是整数值
  • 集合对象保存的元素个数不超过512个

(第二个条件上限值可以修

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Redis设计规范可以根据具体的使用场景和需求来确定,下面是一些常见的设计规范: 1. 键的命名规范:选择一个有意义的键名,以便于维护和理解。可以使用统一的命名前缀来区分不同的业务或模块。 2. 数据结构的选择:根据实际需求选择适合的数据结构来存储数据。Redis支持多种数据结构,如字符串(string)、哈希(hash)、列表(list)、集合(set)、有序集合(sorted set)等。 3. 内存优化:由于Redis是基于内存的数据库,需要合理使用内存。可以通过设置过期时间、压缩数据、使用合适的数据结构等方式来减少内存占用。 4. 数据持久化:Redis支持两种方式的数据持久化,即快照(snapshotting)和AOF(Append-Only File)。可以根据实际情况选择合适的持久化方式,并设置适当的保存频率和策略。 5. 高可用性和容灾:可以通过主从复制(replication)和哨兵(sentinel)机制来实现Redis的高可用性和容灾。设置合适的主从配置和哨兵监控可以保证系统的稳定性和可用性。 6. 性能优化:可以通过批量操作、管道(pipeline)、使用连接池等方式来提高Redis的性能。同时,合理设置连接超时时间、最大连接数等参数也能够优化性能。 7. 安全性保护:为了保护数据的安全性,可以通过密码认证、限制访问IP、使用SSL加密等方式加强安全性。 需要根据具体场景和需求进行设计和调优,以上只是一些常见的设计规范,具体还需根据实际情况来确定。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值