【原文】https://www.toutiao.com/i6594624365906625032/

概述

在前文《Redis字符串类型内部编码剖析》之中已经剖析过 Redis最基本的 String类型的内部是怎么编码和存储的,本文再来阐述 Redis中使用 最为频繁的数据类型:哈希(或称散列),在Redis内部是怎么存的。

  • 实验源码环境:Redis 4.0.10

注: 本文首发于 My Personal Blog,欢迎光临 小站

本文内容脑图如下:



哈希类型内部编码详情

对于 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

OK

127.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 512

hash-max-ziplist-value 64

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

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

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

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

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

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


OBJ_ENCODING_ZIPLIST 编码

Ziplist 压缩列表是一种紧凑编码格式,总体思想是时间换空间,即以部分读写性能为代价,来换取极高的内存空间利用率,因此只会用于 字段个数少,且字段值也较小 的场景。

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

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



OBJ_ENCODING_HT 编码

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

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

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

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

  • 关于哈希节点(dictEntry)

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

  • 关于dictType

  • Redis如何计算Hash值

Redis计算Hash的源代码如下:

这是一个 C语言宏定义,其实幕后真正承担 Hash值计算的是上面介绍的 dictType结构体中的函数指针 hashFunction。

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

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

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

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

【转】Redis学习---哈希结构内存模型剖析的更多相关文章

  1. java并发学习--第十章 java内存模型的内存语义

    一.锁的内存语义 所为的java内存模型的内存语义指的就是在JVM中的实现原则. 锁的内存语义:锁除了让临界区互斥执行外,还可以让释放锁的线程向获取同一个锁的线程发送消息. 我们把上面这句话再整理下: ...

  2. Redis学习---基础学习[all]

    什么是NoSQL型数据库 NoSQL数据库---NoSQL数据库的分类 Redis学习---NoSQL和SQL的区别及使用场景 Redis学习---负载均衡的原理.分类.实现架构,以及使用场景 什么是 ...

  3. JVM学习总结一——内存模型

    JVM是java知识体系的基石之一,任何一个java程序的运行,都要借助于他.或许对于我这种初级程序员而言,工作中很少有必要刻意去关注JVM,然而如果能对这块知识有所了解,就能够更清晰的明白程序的运行 ...

  4. 【CUDA 基础】4.1 内存模型概述

    title: [CUDA 基础]4.1 内存模型概述 categories: - CUDA - Freshman tags: - CUDA内存模型 - CUDA内存层次结构 - 寄存器 - 共享内存 ...

  5. DirectX11 With Windows SDK--29 计算着色器:内存模型、线程同步;实现顺序无关透明度(OIT)

    前言 由于透明混合在不同的绘制顺序下结果会不同,这就要求绘制前要对物体进行排序,然后再从后往前渲染.但即便是仅渲染一个物体(如上一章的水波),也会出现透明绘制顺序不对的情况,普通的绘制是无法避免的.如 ...

  6. 深入学习Redis(1):Redis内存模型

    前言 Redis是目前最火爆的内存数据库之一,通过在内存中读写数据,大大提高了读写速度,可以说Redis是实现网站高并发不可或缺的一部分. 我们使用Redis时,会接触Redis的5种对象类型(字符串 ...

  7. 深入学习Redis:Redis内存模型

    每天学习一点点 编程PDF电子书.视频教程免费下载:http://www.shitanlife.com/code 一.Redis内存统计 工欲善其事必先利其器,在说明Redis内存之前首先说明如何统计 ...

  8. 【转】深入学习Redis(1):Redis内存模型

    原文:https://www.cnblogs.com/kismetv/p/8654978.html 前言 Redis是目前最火爆的内存数据库之一,通过在内存中读写数据,大大提高了读写速度,可以说Red ...

  9. 【深入学习Redis】Redis内存模型

    前言 Redis是目前最火爆的内存数据库之一,通过在内存中读写数据,大大提高了读写速度,可以说Redis是实现网站高并发不可或缺的一部分. 我们使用Redis时,会接触Redis的5种对象类型(字符串 ...

随机推荐

  1. [Python学习笔记-004] 可变参数*args和**kwargs

    在Python中,可变参数的传递使用*args和**kwargs来实现,其中: *args表示任意个位置参数(positional argument),被表示为一个只读的数组(tuple): **kw ...

  2. java中类的加载顺序介绍(ClassLoader)

    转自:http://blog.csdn.net/eff666/article/details/52203406 1.ClassNotFoundExcetpion  我们在开发中,经常可以遇见java. ...

  3. 利用docker hub做中转拉取google的k8s镜像

    1.背景 部署kubernetes,需要FQ.但是在初始化的时候,即是FQ了有的镜像pull依然会超时,导致初始化失败.而你又不想使用国内的一些镜像源,因为更新不及时.很多新功能和插件都不会包括,只想 ...

  4. MAMP 安装phpredis 扩展

    phpredis扩展包地址: https://github.com/nicolasff/phpredis 1.下载php源码 (http://php.net/releases/  选择php5.5.1 ...

  5. 回头再看看babel的实现原理

    一.前言 babel在大家的工作中应该没少用,但是为什么它能将ES6转成ES5呢?一个有态度的前端er肯定会想抛开迷雾,看看其中的奥秘. 记得很早前自己有去了解过相关方面的内容,但是时间久远,现在已是 ...

  6. GCD之同步异步

    博客地址:http://blog.csdn.net/chaoyuan899/article/details/12554603

  7. MFC函数—CSingleDocTemplate

    前提:在InitInstance() 函数的初始化过程中,我们可以看到代码CSingleDocTemplate* pDocTemplate; pDocTemplate = new CSingleDoc ...

  8. Jquery插件的使用及制作插件

    常用插件 插件:jquery不可能包含所有的功能,我们可以通过插件扩展jquery的功能. jQuery有着丰富的插件,使用这些插件能给jQuery提供一些额外的功能. jquery.color.js ...

  9. 2.移植3.4内核-支持烧写yaffs2,裁剪内核并制作补丁

    在上章-制作文件系统,并使内核成功启动jffs2文件系统了 本章主要内容如下: 1)使内核支持yaffs2文件系统 2)裁剪内核 3)制作内核补丁 1.首先获取yaffs2源码(参考git命令使用详解 ...

  10. 如何调试JDK源代码并查看局部变量值

    如下图: 按F5进入String.startsWith,如下: 点“Edit Source Lookup Path” 附加源代码,如下图: 附加上源代码后如下: 可以看到,当鼠标放在“prefix”上 ...