Redis数据类型及编码格式——Hash篇

写在前面

以下的知识都是建立在目前线上稳定版6.2.6版本的

而目前github上的redis源代码又更新优化了许多,譬如:hash数据类型默认的编码格式已经被替换为listpack,hashtable数据结构也被重新优化(哈希表对象dictht被废弃,字典dict直接引用dictEntry节点)

Redis hash数据类型-编码格式

redis中hash数据类型使用了两种编码格式:ziplist(压缩列表)、hashtable(哈希表)

具体使用哪种编码格式,由redis根据情况自己选择更合适的编码格式,这对于上层用户完全透明

  • ziplist,又称为压缩列表,是hash数据类型默认使用的编码格式,当满足某种条件后,redis会将ziplist优化为hashtable编码格式,我们也可通过配置来更改达到条件的阈值
    hash-max-ziplist-entries 512  //配置当field-value超过512时(合起来1024),使用hashtable编码
                                  //至于为什么是1024,与ziplist有关,后面会讲述
    hash-max-ziplist-value 64  //配置当key的单个field或value长度超过64时,使用hashtable编码
  • hashtable,又称为哈希表,是hash数据类型中的数据过多或多长时由ziplist优化而来,底层数据结构与ziplist完全不同,且数据结构不可逆(也就是某个key的数据从ziplist演变为hashtable后,就算其数据减少到了ziplist可接受范围,也不会再从hashtable还原为ziplist格式)

ziplist

ziplist(压缩列表)是一个经过特殊编码的双向链表,用于存储字符串或整数,可以提高存储效率。

与普通双向链表的对比

  • 内存开销不同,普通双向链表的元素一般需要两个指针,这会占用额外的内存,在数据较小的情况下,可能指针对于内存的占用比数据还大,有点得不偿失。而ziplist压缩列表,是一块连续的内存,数据之间紧密相连,不需要指针这种额外的开销。
  • 遍历速度不同,普通链表内的元素一般是分开的,通过指针来查找到下一个元素。而ziplist是在一块连续内存上,所以内部元素遍历起来更快,并且由于ziplist独特的数据结构,它存储了末尾元素的偏移位置,所以它可以轻松定位到末尾元素,所以它获取首尾元素时所需的时间复杂度为O(1)。

ziplist的缺点

  • 元素变动,虽然ziplist可以有效提高存储效率,可是这种数据项紧密相连的存储结构不擅长做修改操作,一旦元素发生变动,极大概率会引发内存重新分配造成数据拷贝,从而降低性能
  • 数据项过多,当它数据项过多时,遍历查找指定元素也会耗时更久,所以这种数据结构也不宜存储过多的元素

因此,虽然redis精心设计了ziplist这种存储效率很高的数据结构,但是只会在适宜的场景下使用,当数据项过多或元素过长时,redis会将ziplist编码格式优化为更加合适的编码格式

ziplist数据结构

ziplist主要由下图几个部分组成,各部分所占字节如图所示:

字节存储的是16进制数据,最大值为0XFF,也就是255,而类似zlen字段占用两个字节,所能表述的最大值也就是FF FF,也就是2^16 - 1,也就是65535,当小于这个值时,zlen表示entry数据项的个数,当这个值为最大值时,zlen已经不能表示entry数据项的大小了,只能通过遍历可知。

entry数据项数据结构

entry数据项主要由下图几个部分构成:

我们来对其进行简单介绍:

1.首先是prevlen,它存储的是前一个数据项entry的长度,占用1或5个字节,可依据此字段进行反向遍历

  • 如果长度在0XFE也就是254之内,则prevlen占用1个字节。
  • 如果长度大于等于254,则prevlen占用5个字节。此时,第一个字节值设为0XFE(254),意味着使用后面更大的值,而剩余的4个字节用来表示前一个数据项的长度。(注:至于为什么不使用0XFF,也就是255,是因为255作为ziplist的结尾符,如果在遍历读取entry时,由于entry本身长度就不固定,那么此时就可能将其误认为结尾符,从而引发错误)

2.其次是encoding,这个字段相对复杂,它根据entry-data存入的是整数还是字符串会有不同的格式,所占的字节数也不相同,一般通过第一个字节就可确定数据项是整数还是字符串,整数的话,第一个字节的前2bit是11,否则为字符串

如果为字符串时,占用字节不固定

  • 字符串长度小于64时,占用1个字节,二进制结构为00pppppp,后6bit表示长度,最高可表示2^6 - 1,也就是63
  • 字符串长度小于16384时,占用2个字节,二进制结构为01pppppp qqqqqqqq,后14bit表示长度,最高可表示2^14 - 1,也就是16283
  • 字符串长度大于等于16384时,占用5个字节,二进制结构为10000000 qqqqqqqq rrrrrrrr ssssssss tttttttt,第一个字节后6bit不使用,后面4个字节的32bit位用来表示长度,最高可表示2^32 - 1,也就是4294967295

如果为整数时,都只占1字节,前2bit确定是否整数节点,后6bit确定整数节点的类型

  • 二进制结构为11000000,值为0XC0,entry-data存储int16_t类型数据(占用2字节)
  • 二进制结构为11010000,值为0XD0,entry-data存储int32_t类型数据(占用4字节)
  • 二进制结构为11100000,值为0XE0,entry-data存储int64_t类型数据(占用8字节)
  • 二进制结构为11110000,值为0XF0,entry-data存储3个字节的整数
  • 二进制结构为11111110,值为0XFE,entry-data存储1个字节的整数
  • 二进制结构为1111xxxx,这是特殊情况,此时xxxx的值在0001至1101之间(因为0000和1110都已经被上面使用了),所以xxxx换算为十进制也就是1-13,而数值应该从0开始,所以得剪1,代表的就是0-12这几个值。此时xxxx直接用来表示entry-data的数据值,也就是不存在entry-data了

3.最后是entry-data,存储整数或字节数组。

  • 当存入的是字符串时,会存入每个字符在ASCII表中对应的16进制值,然后形成一个字节数组。
  • 当存入的是数字时,如果是上面所讲的特殊情况(即encoding为1111xxxx这种),那么该字段不存在,否则会按所属情况存储不同类型的数据,数据类型不同所占用字节也不同

ziplist数据结构实例分析

例如:逐步分析下图中的ziplist字节数据

  1.  zlbytes,4个字节,由于redis采用是小端模式存储,所以实际应为00 00 00 1a,转化为十进制是26,表示该ziplist总共有26个字节
  2. zltail,4个字节,00 00 00 0c转化为十进制是12&#
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Redis支持多种数据类型,包括string、hash、list、set和sorted set等。其中,string是最基本、最简单的数据类型,用于存储字符串。 Hash是用于存储键值对的数据结构,其中的value只能存储字符串,不允许存储其他数据类型,也不存在嵌套现象。每个hash可以存储232 - 1个键值对,并可以灵活添加或删除对象属性。但需要注意的是,hash类型并不适合存储大量对象,也不应该将hash作为对象列表使用,因为遍历整体数据的效率可能会较低。 除了string和hash类型,Redis还支持list、set和sorted set等数据类型。List是一个有序的字符串列表,可以进行插入、删除和查找等操作。Set是一个无序的字符串集合,可以进行元素的添加、删除和查找操作,并且不允许重复元素的存在。Sorted Set是一个有序的字符串集合,每个元素都有一个对应的score,可以根据score进行排序和范围查找。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [Redis 数据类型](https://blog.csdn.net/weixin_52851967/article/details/122670564)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值