Redis 底层数据结构

1.编码方式

我们可以通过Type命令查看当前键的数据结构类型:分别是 string(字符串),hash(哈希),list(列表),set(集合),zset(有序集合·),但是只是Redis对外的数据结构,那这些数据结构底层编码又是如何实现的呢?

 

1.字符串内部编码

字符串内部包含三中编码方式:

  • int:8个字节的长整形
  • embstr: 小于等于44个字节的字符串
  • raw:大于44个字节的字符串
127.0.0.1:6379[1]> object encoding age
"int"
127.0.0.1:6379[1]> object encoding title<44
"embstr"
127.0.0.1:6379[1]> object encoding title=44
"embstr"
127.0.0.1:6379[1]> object encoding title>44
"raw"

//get the content
127.0.0.1:6379[1]> get title=44
"11111111112222222222333333333344444444445555"
127.0.0.1:6379[1]> get title<44
"1111111111222222222233333333334444444444555"
127.0.0.1:6379[1]> get title>44
"111111111122222222223333333333444444444455555"
127.0.0.1:6379[1]> get age
"36"

 2.哈希的内部编码

哈希类型内部编码包含两种方式

  • ziplist(压缩列表):当哈希类型元素小于hash-max-entries配置(default 512)同时所有fields对应的value都小于hash-max-ziplist-value配置(默认64字节时)redis会使用ziplist作为哈希的内存实现。ziplist使用更加紧凑的结构实现多个元素的连续存储,所以在节省内存方面比hashtable更加优秀
  • hashtable(哈希表):当哈希类型不满足ziplist的条件时(元素多余512个或至少有一个field对应的value内容超过64字节时),redis都会使用hashtable作为哈希的内部实现
127.0.0.1:6379[1]> hset hashkey<512 name huery
(integer) 1
127.0.0.1:6379[1]> object encoding hashkey<512
"ziplist"

127.0.0.1:6379[1]> object encoding hashkey
"hashtable"
127.0.0.1:6379[1]> hgetall hashkey
1) "title"
2) "life is shit, but god is forever"
3) "content"
4) "life is shit, but god is forever111111111111111111111111111111111111111111111111111111111111"


3.列表的内部编码

最新版本的Redis列表的内部编码只有一种:

  • quicklist(快速列表):快速列表是ziplist和linkedlist的混合体,它仍然是一个双向链表,它将链表划分为端,每个段都成为一个快速列表节点,每个quicklist节点都使用ziplist进行紧凑存储,多个quicklist节点通过双向指正串联连接。
    127.0.0.1:6379[1]> lrange listkey 0 8
    1) "1111111111222222222233333333334444444444555555555566666666667777777777888888888899999999990000000000111111111122222222223333333333"
    2) "1111111111222222222233333333334444444444555555555566666666667777777777888888888899999999990000000000"
    3) "111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999"
    4) "11111111112222222222333333333344444444445555555555666666666677777777778888888888"
    5) "1111111111222222222233333333334444444444555555555566666666667777777777"
    6) "11111111112222222222333333333344444444445555555555666666666677777"
    7) "1111111111222222222233333333334444444444555555555566666"
    8) "jack"
    127.0.0.1:6379[1]> object encoding listkey
    "quicklist"
    127.0.0.1:6379[1]> lpush listkey<512 hubery
    (integer) 1
    127.0.0.1:6379[1]> object encoding listkey<512
    "quicklist"

 4.集合的内部编码

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

  • intset(整数集合):当集合中元素都是整数且元素个数小于set-max-intset-entries配置(默认512时),redis会选用intset作为集合内部实现,从而减少内存的使用
  • hashtable(哈希表):当集合类型无法满足intset的条件时,redis就会采用hashtable作为集合的内部实现
    127.0.0.1:6379[1]> smembers intsetkey
    1) "1"
    2) "2"
    3) "3"
    4) "4"
    5) "5"
    6) "6"
    7) "7"
    8) "8"
    9) "9"
    127.0.0.1:6379[1]> object encoding intsetkey
    "intset"
    127.0.0.1:6379[1]> smembers setkey
    1) "1111111111222222222233333333334444444444555555555566666666667777777777788888888889999999999"
    2) "111111111122222222223333333333444444444455555555556666666666777777"
    3) "abc"
    127.0.0.1:6379[1]> object encoding setkey
    "hashtable"
    

5.有序集合内部编码 

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

  • ziplist(压缩列表):当有序集合的元素个数小于zset-max-entries配置(默认128个),同时每个元素的值小于zset-max-ziplist-value配置(默认64字节)时,Redis会使用ziplist来作为有序集合的内部实现,ziplist可以有效减少内存使用
  • skiplist(跳跃表):当ziplist条件不满足时(即有序集合内部元素超过128个或存在某个某个元素的值大于64字节)时,有序集合会使用skiplist作为内部实现,此时zip的读写效率会下降
127.0.0.1:6379[1]> zrange sortedkeyset<64 0 3
1) "stephen"
2) "james"
3) "dulant"
127.0.0.1:6379[1]> object encoding sortedkeyset<64
"ziplist"

127.0.0.1:6379[1]> zrange sortedsetkey 0 3
1) "hahaha"
2) "00000000001111111111222222222233333333334444444444555555555566666"
127.0.0.1:6379[1]> object encoding sortedsetkey
"skiplist"

2.基本数据结构

SDS String

Redis使用SDS(simple dynamic string)而不是C语言的字符串格式(以空字符为结尾的字符数组)来最为Redis的默认字符串表示,SDS具有以下特点:

  • 可以存储位数组(实现BITOP和Hyperloglog),字符串,整数和浮点数,其中超过64位的整数和超过IEEE754标准的浮点数使用字符串表示
  • 具有int,embstr和raw三种形式可选,其中int表示用于存储小于等于64位的整数,embstr用来存储比较短的位数组(44*8位)和字符串,而其他格式的值则由raw格式储存
  • 比起C语言的字符串格式,SDS具有以下四个优点:
  1. 常数复杂度获取长度值
  2. 不会引起缓冲区溢出
  3. 通过预分配和惰性释放两种策略来减少内存重分配的执行次数
  4. 可以存储二进制位 

字典 Hash/Set

Redis的字典实现具有以下特性

  • 查找,添加,删除键值对的复杂度为O(1),键和值都是字符串对象
  • 使用散列表(hashtable)为底层实现,使用链地址法(separate chaining)来解决间冲突
  • Redis会在不同的地方使用不同的散列算法,其中最常用的是Murmurhash2算法
  • 在键值对数量大增或大减的时候会对散列表进行重新散列(rehash),并且这个rehash是渐进式,分多次进行的,不会在短时间内耗费大量CPU时间,造成服务器阻塞

压缩列表(ziplist)Hash/List/SortedSet

压缩列表具有以下特点:

  • 压缩列表包含的项都是有序的,列表的两端分别为表头和表尾
  • 每个项可以存储一个字符串,整数或者浮点数
  • 可以从表头开始或者从表尾开始遍历整个压缩列表,复杂度为O(N)
  • 地位压缩列表中指定索引上的项,复杂度为O(N)
  • 使用压缩列表来存储值消耗的内存比使用双向链表来存储值小号的内存要少

 

双向链表(linkedlist)List

Redis的双向链表具有以下特性

  • 双向,无环,带有表头和表尾指针
  • 一个链表包含多项,每个项都是一个字符串对象,换句话来说,一个列表对象可以包含多个字符串对象
  • 可以从表头或表尾遍历整个链表,复杂度为O(N)
  • 定位特定索引上的项,复杂度为O(N)
  • 链表带有长度记录属性,获取链表的当前长度的复杂度为O(1)

整数集合(intset)Set

整数集合具有以下特点:

  • 整数集合只能是整数(最大为64位),并且集合中不会出现重复的元素
  • 集合的低层使用有序的整数数组来表示
  • 数组的类型会随着新添加元素的类型而改变:举例,如果集合中位长度最大的元素可以使用16位整数来保存,那么数组的类型就是int16_t,而如果集合中位长度最大的元素可以使用32位整数来保存的话,那么数组的类型就是int32_t,组如此类
  • 数组的类型只会自动增大,但不会减小

跳跃表(skiplist)SortedSet

Redis的跳跃表具有以下特点:

  • 支持平均O(logN)最坏O(N)复杂度的节点查找操作,并且可以通过执行范围性(range)操作来批量的获取有序的节点
  • 跳跃表节点除了实现跳跃表所需的层(level)之外,还具有score属性和obj属性,前者是一个浮点数,用于记录成员的分值,而后者则是一个字符串对象,用来记录成员本身
  • 和字典一起构成ZSET结构,用于实现Redis的有序集合结构,其中字典用于快速获取元素的分值(比如实现ZSCORE命令),以及判断元素是否存在,而跳跃表则用于这行范围操作(比如实现ZRANGE命令)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值