0
点赞
收藏
分享

微信扫一扫

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

booksmg2014 2022-02-22 阅读 85

概述

要了解redis中hash数据类型底层使用的编码格式,首先得了解ziplist、hashtable这两种编码格式

ziplist

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

与普通双向链表的对比

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

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,表示该ziplist中最后一个数据项entry所在的位置是第12个字节
  3. zlen,2个字节,00 02转化为十进制是2,表示该ziplist中有2个数据项entry,如果此值为ff ff时,则说明两个字节不够记录数据项数量了,需遍历才能得知
  4. entry1,2个字节,00转化为十进制是0,表示上一个数据项长度为0。f3转化为二进制是11110011,通过上面encoding介绍可知,它属于1111xxxx这个特殊情况,那么0011二进制即为所存数据值,转化为十进制是3,3-1=2,所以可以知道redis中该key的第一个值存的是2
  5. entry2,13个字节,02转化为十进制是2,表示上一个数据项长度为2。0b转化为二进制是00001011,通过encoding介绍可知,它存入的是字符串且小于64,所以直接通过第一个字节的后6位bit可算出十进制是11,说明entry-data长度是11位。然后根据16进制数据去ASCII表中一个个寻找(例如:16进制为48的在ASCII中表示H),可得知字节数组合起来是Hello World,所以可以知道redis中该key的第二个值存的是Hello World
  6. 0XFF,读到这里说明该ziplist到这里就结束了

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,又称为哈希表

未完待续

hashtable编码格式有时间再补上来

举报

相关推荐

0 条评论