Redis 数据类型和底层数据结构

底层数据结构

dict

参考:Redis内部数据结构详解(1)——dict

dict 是一个用于维护 key 和 value 映射关系的数据结构,与很多语言中的 Map 或 dictionary 类似。Redis 的一个 database 中所有 key 到 value 的映射,就是使用一个 dict 来维护的。不过,这只是它在 Redis 中的一个用途而已,它在 Redis 中被使用的地方还有很多。比如,一个 Redis hash 结构,当它的field 较多时,便会采用 dict 来存储。再比如,Redis 配合使用 dict 和 skiplist 来共同维护一个 sorted set。

在这里插入图片描述

增量式重哈希:查找、插入、删除都会触发

在这里插入图片描述

参考:极客时间 Redis核心技术与实战 笔记(实践篇 数据结构)

SDS

参考:Redis内部数据结构详解(2)——sds

全称 Simple Dynamic String,所有key都是用sds存储的

robj

参考:Redis内部数据结构详解(3)——robj

从 Redis 的使用者的角度来看,一个 Redis 节点包含多个 database (非cluster模式下默认是16个,cluster模式下只能是1个),而一个 database 维护了从 key space 到 object space 的映射关系。这个映射关系的 key 是 string 类型,而 value 可以是多种数据类型,比如:string, list, hash等。我们可以看到,key的类型固定是string,而value可能的类型是多个。

一个database内的这个映射关系是用一个dict来维护的。dict的key固定用一种数据结构来表达就够了,这就是动态字符串sds。而value则比较复杂,为了在同一个dict内能够存储不同类型的value,这就需要一个通用的数据结构,这个通用的数据结构就是robj(全名是redisObject)。举个例子:如果value是一个list,那么它的内部存储结构是一个quicklist(quicklist的具体实现我们放在后面的文章讨论);如果value是一个string,那么它的内部存储结构一般情况下是一个sds。当然实际情况更复杂一点,比如一个string类型的value,如果它的值是一个数字,那么Redis内部还会把它转成long型来存储,从而减小内存使用。而一个robj既能表示一个sds,也能表示一个quicklist,甚至还能表示一个long型。

ziplist

参考:Redis内部数据结构详解(4)——ziplist
ziplist的内存结构

quicklist

参考:Redis内部数据结构详解(5)——quicklist

skiplist

跳表复杂度? O(logn)

参考:Redis内部数据结构详解(6)——skiplist
在这里插入图片描述

intset

参考:Redis内部数据结构详解(7)——intset

intset与ziplist相比:

  • ziplist可以存储任意二进制串,而intset只能存储整数。
  • ziplist是无序的,而intset是从小到大有序的。因此,在ziplist上查找只能遍历,而在intset上可以进行二分查找,性能更高。
  • ziplist可以对每个数据项进行不同的变长编码(每个数据项前面都有数据长度字段len),而intset只能整体使用一个统一的编码(encoding)。

对于小集合使用intset来存储,主要的原因是节省内存。特别是当存储的元素个数较少的时候,dict所带来的内存开销要大得多(包含两个哈希表、链表指针以及大量的其它元数据)。所以,当存储大量的小集合而且集合元素都是数字的时候,用intset能节省下一笔可观的内存空间。

实际上,从时间复杂度上比较,intset的平均情况是没有dict性能高的。以查找为例,intset是O(log n)的,而dict可以认为是O(1)的。但是,由于使用intset的时候集合元素个数比较少,所以这个影响不大。

Redis 五种常见数据类型

在这里插入图片描述

String

底层实现:SDS,整数

Hash

底层实现:ziplist,dict

两个 Redis 配置:

hash-max-ziplist-entries 512
hash-max-ziplist-value 64

这个配置的意思是说,在如下两个条件之一满足的时候,ziplist会转成dict:

  • 当hash中的数据项(即field-value对)的数目超过512的时候,也就是ziplist数据项超过1024的时候(请参考t_hash.c中的hashTypeSet函数)。
  • 当hash中插入的任意一个value的长度超过了64的时候(请参考t_hash.c中的hashTypeTryConversion函数)。

hash 和 string 的存储效率

我们在网上很容易找到这样一些技术文章,它们会说存储一个对象,使用 hash 比 string 要节省内存。实际上这么说是有前提的,具体取决于对象怎么来存储。如果你把对象的多个属性存储到多个 key 上(各个属性值存成 string),当然占的内存要多。但如果你采用一些序列化方法,比如 Protocol Buffers,或者 Apache Thrift,先把对象序列化为字节数组,然后再存入到 Redis 的 string 中,那么跟hash 相比,哪一种更省内存,就不一定了。

当然,hash 比序列化后再存入 string 的方式,在支持的操作命令上,还是有优势的:它既支持多个 field 同时存取(hmset/hmget),也支持按照某个特定的 field 单独存取(hset/hget)。

实际上,hash 随着数据的增大,其底层数据结构的实现是会发生变化的,当然存储效率也就不同。在field比较少,各个 value 值也比较小的时候,hash 采用 ziplist 来实现;而随着 field 增多和 value 值增大,hash 可能会变成 dict 来实现。当 hash 底层变成 dict 来实现的时候,它的存储效率就没法跟那些序列化方式相比了。

List

底层实现:3.2 以前是 ziplist 和 linkedlist,3.2以后是 quicklist

参考:Redis列表list 底层原理

Set

底层实现:intset,dict

Sorted Set

底层实现:ziplist,skiplist

如下两个条件之一满足的时候,ziplist会转成zset(具体的触发条件参见t_zset.c中的zaddGenericCommand相关代码):

  • 当sorted set中的元素个数,即(数据, score)对的数目超过128的时候,也就是ziplist数据项超过256的时候。
  • 当sorted set中插入的任意一个数据的长度超过了64的时候。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值