【原文】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. Spark2.1.0——深入浅出度量系统

    Spark2.1.0--深入浅出度量系统 对于一个系统而言,首先考虑要满足一些业务场景,并实现功能.随着系统功能越来越多,代码量级越来越高,系统的可维护性.可测试性.性能都会成为新的挑战,这时监控功能 ...

  2. ffplay源码分析4-音视频同步

    本文为作者原创,转载请注明出处:https://www.cnblogs.com/leisure_chn/p/10307089.html ffplay是FFmpeg工程自带的简单播放器,使用FFmpeg ...

  3. KMP算法(——模板习题与总结)

    KMP算法是一种改进的模式匹配算法,相比于朴素的模式匹配算法效率更高.下面讲解KMP算法的基本思想与实现. 先来看一下朴素模式匹配算法的基本思想与实现. 朴素模式匹配算法的基本思想是匹配过程中如果该位 ...

  4. 进程监控工具supervisor

    supervisor是一个python编写的进程管理工具, 可以方便的管理和监控进程. supervisor分为服务端supervisord和客户端supervisorctl. supervisor由 ...

  5. 获取当前iframe动态加载文档的href

    Insus.NET想实现一个功能,一个旧的站点A,它有两个网页logon.aspx和Default.aspx(登录成功能访问).由于某些原因,需另建一个新站点B,这个新站点B也有两个网页B_Index ...

  6. JSON跨域解决方案收集

    最近面试问的挺多的一个问题,就是JavaScript的跨域问题.在这里,对跨域的一些方法做个总结.由于浏览器的同源策略,不同域名.不同端口.不同协议都会构成跨域:但在实际的业务中,很多场景需要进行跨域 ...

  7. WCF、WebAPI、WCFREST、WebService之间的区别总结(实用)

    在.net平台下,有大量的技术让你创建一个HTTP服务,像Web Service,WCF,现在又出了Web API.在.net平台下,你有很多的选择来构建一个HTTP Services.我分享一下我对 ...

  8. Win10 注册IIs4.0的解决方案

    随着Win10的出现,越来越多的人装上了Win10,尤其是程序员,由于Win10是一个新的操作系统,但现有软件的兼容性等各方面都是未知,难免会存在很多坑,就拿IIS来说,我刚装完win10系统,然后装 ...

  9. 26.Linux-网卡驱动介绍以及制作虚拟网卡驱动(详解)

    1.描述 网卡的驱动其实很简单,它还是与硬件相关,主要是负责收发网络的数据包,它将上层协议传递下来的数据包以特定的媒介访问控制方式进行发送, 并将接收到的数据包传递给上层协议. 网卡设备与字符设备和块 ...

  10. mongdb查询操作

    1.db.table.find() 查询所有信息 db.table.findOne()查询第一个信息2.db.table.find({条件},{要显示列}) db.table.find({},{_id ...