3.2 Redis的五种数据结构的内部编码

Redis的五种数据结构的内部编码

type命令实际返回的就是当前键的数据结构类型,它们分别是string(字符串)hash(哈希)list(列表)set(集合)zset(有序集合) 等,这些只是Redis对外的数据结构。
在这里插入图片描述
实际上每种数据结构都有自己底层的内部编码实现,而且是多种实现,这样Redis会在适合的场景选择合适的内部编码。

可以看到每种数据结构都有两种以上的内部编码实现,例如string数据类型就包含了raw、int、embstr三种内部编码。

同时,有些内部编码可以作为多种外部数据结构的内部实现,例如ziplist就是hash、list和zset共有的内部编码。

我们可以通过object encoding命令查询内部编码:

internal-156.sdjnsoft.com:16379> set hello redis
OK
internal-156.sdjnsoft.com:16379> type hello
string
internal-156.sdjnsoft.com:16379> object encoding hello
"embstr"

internal-156.sdjnsoft.com:16379> hmset user:100001 name zhang3 age 28
OK
internal-156.sdjnsoft.com:16379> type user:100001
hash
internal-156.sdjnsoft.com:16379> object encoding user:100001
"ziplist"
internal-156.sdjnsoft.com:16379> 

可以看到键hello对应值得内部编码是empstr,键user:100001的内部编码是ziplist。

Redis这样设计有两个好处:

  1. 可以改进内部编码,而对外的数据结构和命令没有影响,这样一旦开发者开发出优秀的内部编码,无需改动外部数据结构和命令。
  2. 多种内部编码实现可以在不同场景下发挥各自的优势,例如ziplist比较省内存,但是在列表元素比较多的情况下,性能会有所下降,这时候Redis会根据配置将选项将列表类型的内部实现转换为linkedList 。

五种数据结构的内部编码方式

1. 字符串的内部编码

字符串类型的内部编码有3种:

  • int: 8个字节的长整型。
  • embstr: 小于等于39个字节的字符串。
  • raw: 大于39个字节的字符串。

Redis会根据当前值得类型和长度决定内部编码实现。

(1)整数类型示例如下:

internal-156.sdjnsoft.com:16379> set str:int 123456789
OK
internal-156.sdjnsoft.com:16379> object encoding str:int
"int"

(2)短字符串示例如下:

internal-156.sdjnsoft.com:16379> set str:embstr "hello world"
OK
internal-156.sdjnsoft.com:16379> object encoding str:embstr
"embstr"

(3)长字符串示例如下:

internal-156.sdjnsoft.com:16379> set str:raw "概要设计有多种方法。在早期有模块化方法、功能分解方法;在60年代后期提出了面向数据流和面向数据结构的设计方法;近年来又提出面向对象的设计方法等。"
OK
internal-156.sdjnsoft.com:16379> object encoding str:raw
"raw"
2. 哈希的内部编码

哈希类型的内部编码有两种:

  • ziplist(压缩列表):当哈希类型元素个数小于hash-max-ziplist-entries配置(默认512个),同时所有值都小于hash-max-ziplist-value配置(默认64字节)时,所以在节省内存方面比hashtable更加优秀。
  • hashtable(哈希表):当哈希类型无法满足ziplist的条件时,Redis会使用hashtable作为hash的内部实现,因此此时ziplist的读写效率会下降,而hashtable的读写时间复杂度为O(1).

O(n²)表示当n很大的时候,复杂度约等于Cn²,C是某个常数,简单说就是当n足够大的时候,n的线性增长,复杂度将沿平方增长。

O(n)也是差不多的意思,也就是说n很大的时候复杂度约等于Cn,C是某个常数。

O(1)就是说n很大的时候,复杂度基本就不增长了,基本就是个常量C。
下面展示哈希类型的内部编码,及相应的变化。

(1) 当field个数比较少且没有大的value时,内部编码为ziplist;

internal-156.sdjnsoft.com:16379> hmset user:100002 name zhang3 age 19 sex 男
OK
internal-156.sdjnsoft.com:16379> object encoding user:100002
"ziplist"

(2) 当有value大于64个自己,内部编码会有ziplist变为hashtable;

internal-156.sdjnsoft.com:16379> hmset user:100003 info "概要设计有多种方法。在早期有模块化方法、功能分解方法;在60年代后期提出了面向数据流和面向数据结构的设计方法;近年来又提出面向对象的设计方法等。"
OK
internal-156.sdjnsoft.com:16379> object encoding user:100003
"hashtable"

(3)当field个数超过512,内部编码也会变为hashtable;

// 略.

注意:当一个哈希的编码由ziplist变为hashtable的时候,即使在替换掉所有值,它一直都会是hashtable类型。

3. 列表的内部编码

列表类型的内部编码有两种:

  • ziplist(压缩列表):当哈希类型元素个数小于hash-max-ziplist-entries配置(默认512个),同时所有值都小于hash-max-ziplist-value配置(默认64字节),Redis会使用ziplist作为哈希的内部实现。
  • linkedlist(链表):当列表类型午饭满足ziplist的条件时,Redis会使用linkedlist作为列表的内部实现。

(1)当元素个数较少且没有大元素时,内部编码为ziplist:

internal-156.sdjnsoft.com:16379> rpush list:2 a b c
(integer) 3
internal-156.sdjnsoft.com:16379> object encoding list:2
"ziplist"

(2)当元素个数超过512个,内部编码变为linkedlist:

internal-156.sdjnsoft.com:16379>lpush setkey 1 2 3 ... 513
OK
internal-156.sdjnsoft.com:16379> object encoding listkey
"linkedlist"

(3)当某个元素超过64个字节,内部编码也会变为linkedlist:

internal-156.sdjnsoft.com:16379> rpush list:1 a b "我不再说话,不再思索,但无尽的爱从灵魂中升起,我将远行,走得很远,如同一个吉普塞人,穿过大自然——幸福得如有一位女子同行。"
(integer) 6
internal-156.sdjnsoft.com:16379> object encoding list:1
"linkedlist"

内部编码规则只能升级。不能自动变回ziplist


3.2
  • 3.2之后加入的新的数据结构quicklist,用作redis对外提供的五种数据类型—list的底层实现。
  • 考虑到链表的附加空间相对太高,prev 和 next 指针就要占去 16 个字节 (64bit 系统的指针是 8 个字节),另外每个节点的内存都是单独分配,会加剧内存的碎片化,影响内存管理效率。 后续版本对列表数据结构进行了改造,使用 quicklist 代替了 ziplist 和 linkedlist.
  • quickList 是 zipList 和 linkedList 的混合体,它将 linkedList 按段切分,每一段使用 zipList 来紧凑存储,多个 zipList 之间使用双向指针串接起来。
    在这里插入图片描述
  • quicklist 默认的压缩深度是 0,也就是不压缩。压缩的实际深度由配置参数list-compress-depth决定。为了支持快速的 push/pop 操作,quicklist 的首尾两个 ziplist不压缩,此时深度就是 1。如果深度为 2,就表示 quicklist 的首尾第一个 ziplist 以及首尾第二个 ziplist 都不压缩。
  • quicklist 内部默认单个 ziplist 长度为 8k 字节,超出了这个字节数,就会新起一个 ziplist。  ziplist 的长度由配置参数 list-max-ziplist-size 决定。
4. 集合的内部编码

集合类型的内部编码有两种:

  • intset(整数集合): 当集合中的元素都是整数且元素个数小于set-max-intset-entries(默认512个)时,Redis会选用intset来作为集合内部实现,从而减少内存使用。
  • hashtable(哈希表):当集合类型无法满足intset的条件时,Redis会使用hashtable作为集合的内部实现。
    下面来举例说明:

(1)当元素个数较少且都为整数时,内部编码为intset;

internal-156.sdjnsoft.com:16379> sadd set:intset 1 2 3 4 5
(integer) 5
internal-156.sdjnsoft.com:16379> object encoding set:intset
"intset"

(2) 当元素个数超过512个,内部编码变为hashtable ;

internal-156.sdjnsoft.com:16379>sadd set:hashtable 1 2 3 4 5 6 7...  511 512 513
(integer) 513
internal-156.sdjnsoft.com:16379> object encoding set:hashtable
"hashtable"

(3) 当某个元素不为整数时,内部编码也变为hashtable;

internal-156.sdjnsoft.com:16379> sadd set:hashtable a 1 2 bv 3 4
(integer) 6
internal-156.sdjnsoft.com:16379> object encoding set:hashtable
"hashtable"
5. 有序集合的内部编码

有序集合类型的内部编码有两种:

  • ziplist(压缩列表):当有序集合的元素个数小于zset-max-ziplist-entries配置(默认128个),同时每个元素的值小于zset-max-ziplist-value配置(默认64个字节)时,Redis会用ziplist来作为有序集合的内部实现,ziplist可以有效减少内存使用。
  • skiplist(跳跃表):当ziplist条件不满足时,有序结合会使用skiplist作为内部实现,因为此时zip读写效率会下降。

下面来举例来说明:

(1)当元素个数较小且每个元素较小时,内部编码为ziplist

internal-156.sdjnsoft.com:16379> zadd zset:ziplist 10 a 20 b 30 c
(integer) 3
internal-156.sdjnsoft.com:16379> object encoding zset:ziplist
"ziplist"

(2) 当元素个数超过128个,内部编码变为skiplist;

...待输入...

(3) 当某个元素大于64个时,内部编码会变为skiplist;

internal-156.sdjnsoft.com:16379> zadd zset:skiplist 10 a 20 b 30 c 40 "asdasdaskdjalksnd;kjsadnbg;kjdanfg;dksnhg;fdksnhsdfkjhns;dfglkhnsdf'lhk"
(integer) 4
internal-156.sdjnsoft.com:16379> object encoding zset:skiplist
"skiplist"

部分转自 Redis的五种数据结构-明王不动心

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值