深入理解:Redis哈希结构内存模型剖析

1540197692318dacb24675e


本文主要阐述 Redis中使用 最为频繁的数据类型:哈希(或称散列),在Redis内部是怎么存的。

本文内容脑图如下:

15401975841808d7520bdf6



哈希类型内部编码详情

对于 Redis的常用  5  种数据类型(String、Hash、List、Set、sorted set),每种数据类型都提供了  最少两种  内部的编码格式,而且每个数据类型内部编码方式的选择  对用户是完全透明的 ,Redis会根据数据量自适应地选择较优化的内部编码格式。

如果想查看某个键的内部编码格式,可以使用 OBJECT ENCODING keyname 指令来进行,比如:

127.0.0.1:6379> 
127.0.0.1:6379> set foo bar
OK127.0.0.1:6379> 
127.0.0.1:6379> object encoding foo // 查看某个Redis键值的编码"embstr"127.0.0.1:6379> 
127.0.0.1:6379>

对于使用最为频繁的 Hash类型,其内部编码方式可能有两种:

  • OBJ_ENCODING_ZIPLIST (压缩列表)

  • OBJ_ENCODING_HT (哈希表)

Redis 会根据 数据量 的情况来 自适应 地选择这两种编码方式中  较优  的一种,而这一切对用户完全透明。

在  数据条目较少 数据值较小  的时候 Redis会采用  压缩列表 (OBJ_ENCODING_ZIPLIST)编码方式进行存储。这里成员"较少",成员值"较小"的标准可以通过如下配置项进行配置:

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

Redis 默认给出了默认值,当然用户可根据实际情况自行配置。

当 Hash类型键的字段个数 < hash-max-ziplist-entries 并且 每个字段名和字段值的长度 < hash-max-ziplist-value 时,Redis 会使用 OBJ_ENCODING_ZIPLIST来存储该键,反之则会转换为 OBJ_ENCODING_HT的编码方式。

口说无凭,我们不妨先来做个实验感受一下吧:

15401975840215455df1145


很明显该实验验证了当  字段值长度大于64 时,编码格式会由 ZIPLIST方式切换为 Hashtable方式。

源码之前,了无秘密,我们再来看一下Redis关于这部分切换的源码实现,那就理解得更加清楚了:

15401975841054a92988c1c


15401975841195e8c622b4f


下面详解 OBJ_ENCODING_ZIPLIST 和 OBJ_ENCODING_HT 这两种编码格式的内部存储模型,知道了其各自特点和优缺点,自然也就明白了Redis内部使用它们的意图。


OBJ_ENCODING_ZIPLIST 编码

Ziplist 压缩列表是一种紧凑编码格式,总体思想是 时间换空间 ,即以部分读写性能为代价,来换取极高的内存空间利用率,因此只会用于  字段个数少 ,且 字段值也较小  的场景。 给大家推荐一个架构交流群: 698581634  进群即可免费获取资料

压缩列表内存利用率极高的原因与其连续内存的特性是分不开的,其典型的内存结构可以用下图形象地展示出来:

1540197584219e85aef2d21


所以如果用 Ziplist来存储 Redis的散列类型的话,元素的排列方式就变成了如下图所示的形象示意图:即key和value都是逻辑连续内存:

1540197584174d1762bd97b



OBJ_ENCODING_HT 编码

OBJ_ENCODING_HT 这种编码方式内部才是真正的哈希表结构,或称为字典结构,其可以实现O(1)复杂度的读写操作,因此效率很高。

在 Redis内部,从 OBJ_ENCODING_HT类型到底层真正的散列表数据结构是一层层嵌套下去的,关系如下:

1540197584226784af57104


这一关系我们可以从 Redis哈希表定义部分的源码来看出:

15401975842118f5a6cd04b


下面来详解一下各个部分:

  • 关于哈希节点(dictEntry)

15401975843555ad717a118


  • 关于哈希表(dictht)和字典(dict)

1540197584185383830dd44


  • 关于dictType

1540197583995960d400434


  • Redis如何计算Hash值

Redis计算Hash的源代码如下:

154019758398877554f4371


这是一个 C语言宏定义,其实幕后真正承担 Hash值计算的是上面介绍的 dictType结构体中的函数指针 hashFunction。 给大家推荐一个架构交流群: 698581634  进群即可免费获取资料

而该 hashFunction函数指针在初始化时会对应被赋值为一个个真实的计算 Hash值的实际函数,就像下面这样:

15401975842645353619875


  • Redis如何计算存取索引Index值

Index值的计算依赖于上面计算得出的 Hash值,代码如下:

1540197716733fbf84bddd8


到此,还有一个 一直非常值得关注的细节 :即字典 dict里总是保存有两个 Hash表结构  ht[2] ,以及与其高度相关的  rehash操作 ,这在下一篇文章里详解。

原文链接:https://my.oschina.net/hansonwang99/blog/1934354

由于能力有限,若有错误或者不当之处,还请大家批评指正,一起学习交流!


来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/31545684/viewspace-2217126/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/31545684/viewspace-2217126/

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值