ICode9

精准搜索请尝试: 精确搜索
首页 > 数据库> 文章详细

redis数据结构底层编码,Api使用,使用场景(5000字)

2021-03-18 21:32:11  阅读:177  来源: 互联网

标签:编码 5000 SDS 对象 redis 列表 Api 字符串


文章目录

redis常用数据结构以及底层编码

redis对象

在这里插入图片描述

在redis中,使用对象来表示数据库中的键值。redis中的对象可以用一个redisobject对象来表示。
在redis中有五种常用的数据类型:

REDIS_String字符串对象
Redis_LIST列表对象
Redis_HASH哈希对象
Redis_SET集合对象
Redis_ZSET有序集合对象

这里需要强调的一点是,上面每种数据结构对象,其内部编码在不同的场景下可以有不同的实现,这里在后面分析具体的底层编码后会有具体的说明。
下图简要说明了redisobject的组成:

字符串对象

字符串对象使用的编码:

编码对象
REDIS_ENCODING_INT当字符串的值保存整数时使用
REDIS_ENCODING_EMBSTR使用embstr编码的简单动态字符
REDIS_ENCODING_RAW使用简单动态字符串实现的字符串对象

简单动态字符串(SDS)是什么?
redis没有使用c语言的字符串,而是自己实现了一个名为简单动态字符串的抽象类型,在redis数据库里面,字符串的键值对在底层都是由SDS实现的,这里需要说明的一点,sds还被用作缓冲区,AOF中的AOF缓冲区,客户端输入,输出缓冲区都是由SDS实现的。

SDS的结构:{
int len //记录字符串的长度,所以在获取字符串长度命令时,其时间复杂度为o(1)
设置和更新SDS长度的工作是由SDS的API在执行时自动完成的。
char buf[] //字节数组,用于保存字符串
int free // 记录buf数组中未使用字节的数量
}

SDS:有效的避免了缓冲区(buf)溢出:

当执行SDS API时,比如执行append命令时,API会先检查SDS的空间是否满足修改所需的要求,如果空间大小不满足的话,API会自动将SDS的空间扩展至执行修改所需的大小。然后才执行实际的修改操作。
修改字符串的值可能带来的问题:

  1. 如果对字符串进行增长,如上述所说,需要进行内存分配来扩展底层buf数组空间的大小,如果没有这一步内存分配,就会造成缓冲区溢出。
  2. 如果对字符串修改后,缩短了字符串,那么就需要重新分配内存释放掉字符串缩短的那部分内存空间,如果没有做,就会产生内存泄漏。

内存分配需要执行系统调用,所以通常是比较耗时的操作,在SDS中,通过未使用空间free,实现了空间预分配和惰性空间释放两种优化策略。

空间预分配

对SDS修改后,SDS的长度(len)将小于1MB,那么程序分配和len属性同样大小的未使用空间,这时SDS的len属性的值,将和free属性的值相同。即相当于把未使用的值扩展为现在buf数组已使用的值,如果下次对字符串进行增长,增长长度小于free的值,就不会进行内存分配,只有在下次buf数组不够用时,才会再次按照上述规则进行再次进行内存分配。

惰性空间释放

当需要缩短SDS保存的字符串时,不会立即使用内存重分配来回收缩短的字节,而是使用free属性将这些字节的数量记录下来,并等待将来使用。这种策略,避免了缩短字符串所需的内存重分配操作,并为可能出现的增长操作提供了优化。

介绍完了SDS,下面我们继续回到字符串对象的介绍,并比较raw编码与embstr编码的区别:
如果字符串对象保存的字符串的值长度>39字节,那么字符串对象将使用一个SDS来保存字符串的值,并将对象的编码设置为raw。
如果字符串对象保存的字符串的值长度<=39字节,那么字符串对象将使用一个SDS来保存字符串的值,并将对象的编码设置为embstr。

embstr与raw两者之间的差异:

embstr:是用于保存短字符串的一种优化编码方式,embstr编码与raw编码一样,都是用redisobject与sds结构来表示字符串对象,但是raw编码会调用两次内存分配函数分别创建redisobject结构和sds结构(所以sds能够表示较长的字符串),而embstr编码则通过调用一次内存分配函数来分配一块连续的内存空间,空间中依次包含redisobject与sds结构。
embstr优点:
将创建字符串对象所需内存分配次数降低了1次,只需要执行一次内存释放函数就可以释放。
说明:
我们可以在redis中使用long,double类型的浮点数,但是浮点数在redis是以字符串值来保存的,如果保存一个浮点数,会先将浮点数转化成字符串值,然后保存转换所得到的的字符串值。
Redis中没有为embstr编码的字符串对象编写任何相应的修改程序(只有int编码的字符串对象和raw编码的字符串对象有修改程序)即embstr编码的字符串对象只是可读的,当对embstr编码的字符串对象执行修改命令时,会先将对象的编码从embstr转换成raw,然后再执行修改命令。

使用场景:

  • 计数器:因为redis的单线程机制,所以不会存在并发技术冲突。
  • 缓存
  • 分布式锁

列表对象:

列表对象编码:

编码对象
REDIS_ENCODING_ZIPLIST使用压缩列表实现的列表对象
REDIS_ENCODING_LinkeList使用双端链表实现的列表对象

编码:

下面介绍列表底层编码:

链表:

在这里插入图片描述在这里插入图片描述
特性:
链表为双端链表,获取某个节点的前端节点与后端节点的复杂度都是o(1)
链表没有环,表头节点的prev指针还有表尾节点的next指针都是null,获取表头节点和表尾节点的复杂度为o(1)
链表自己维护了链表长度属性len,所以获取长度的时间复杂度为o(1)。

使用场景:

发布与订阅功能,慢查询,监视器
redis服务器本身还使用了链表来保存多个客户端的状态信息,以及使用链表来构建客户端输出缓冲区。

压缩列表

在这里插入图片描述在这里插入图片描述在这里插入图片描述
压缩列表是列表键和哈希键的底层实现之一:
当列表只包含少量的列表项,且列表项是小整数值,或者长度比较短的字符串,redis就会使用压缩列表来做列表的底层实现。
当哈希键只包含少量键值对,并且键值对的键和值要么就是小整数值,或者长度比较短的字符串
redis就会使用压缩列表来做哈希键的底层实现。

压缩列表可能出现的问题(连锁更新):

如果在压缩列表中存在连续的几个节点的长度在251-253间,那么它们的pre_length属性都为1个字节,如果其中任意一个节点长度发生变化增加,那么后面的节点的pre_length都需要扩容到5字节,造成一系列节点的连续扩容,会对系统性能造成很大的影响。

哈希对象

编码对象
REDIS_ENCODING_ZIPLIST使用压缩列表实现的对象
REDIS_ENCODING_HT使用字典实现的哈希对象

ziplist

哈希对象编码如果是ziplist,每当有新的键值对加入到哈希对象时,会先将键的压缩列表节点推入到压缩列表表尾,然后将保存了值的压缩列表节点推入到压缩列表表尾。所以保存了同一个键值对的两个节点总是紧挨在一起,键在前,值在后。

hashtable

hastable编码的哈希对象,使用字典作为底层实现,哈希对象中的每个键值对,都使用一个字典键值对来保存。字典的每个键都是一个字符串对象,对象中保存了键值对的键,字典中的每个值都是一个字符串对象,对象中保存了键值对的值。
字典对象结构图:
在这里插入图片描述

转换

当哈希对象中保存的所有键值对的键和值的字符串长度都小于64字节,当哈希对象保存的键值对的数量小于512个,都会使用ziplist作为hash对象底层编码。

集合对象

编码对象
REDIS_ENCODING_INTSET使用整数集合实现的集合对象
REDIS_ENCODING_ZIPLIST使用字典实现的有序集合对象

编码之间的相互转换

当满足集合对象保存的所有元素都是整数值,并且集合对象保存的元素数量不超过512个
时会使用整数集合实现的集合对象,注意当使用字典作为集合的底层实现时,字典中的dictEntry键为元素,值为null。

有序集合对象

编码对象
REDIS_ENCODING_ZIPLIST使用压缩列表实现的有序集合对象
REDIS_ENCODING_SKIPLIST使用跳跃表和字典实现的有序集合对象

这里重点介绍一下skiplist编码的有序集合,它使用zset结构作为底层实现,一个zset结构同时包含一个字典和一个跳跃表。
zset结构中的跳跃表按照分值从小到大保存了所有集合元素,每个跳跃表节点都保存了一个集合元素:跳跃表节点的object属性,保存了元素的成员,跳跃表节点的score属性保存了元素的分值。通过这个跳跃表,就可以进行范围性操作。但是如果我们想要根据元素查询分值,只有跳跃表的话,时间复杂度就会变成o(logn),所以引入字典为有序集合创建一个从成员到分值的映射,字典的dictEntry中的键保存元素,而值保存元素的分值。基于此,就可以用o(1)复杂度查找给定成员的分值。
总的来说,redis选择了用时使用字典和跳跃表两种数据结构来实现有序集合,让有序集合的查找和范围型操作都尽可能快的执行。

编码之间的相互转换:

当有序集合保存的元素数量小于128,并且保存的所有元素成员的长度都小于64字节,会采用ziplist编码实现有序集合,任一条件不满足都会采用skiplist编码。

标签:编码,5000,SDS,对象,redis,列表,Api,字符串
来源: https://blog.csdn.net/weixin_43340717/article/details/114271374

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有