图解redis五种数据结构底层实现

article/2025/9/13 21:58:24

redis有五种基本数据结构:字符串、hash、set、zset、list。但是你知道构成这五种结构的底层数据结构是怎样的吗?今天我们来花费五分钟的时间了解一下。(目前redis版本为3.0.6)

动态字符串SDS

SDS是"simple dynamic string"的缩写。redis中所有场景中出现的字符串,基本都是由SDS来实现的

  • 所有非数字的key。例如 setmsg"hello world" 中的key msg.
  • 字符串数据类型的值。例如`` set msg "hello world"中的msg的值"hello wolrd"
  • 非字符串数据类型中的“字符串值”。例如 RPUSH fruits"apple""banana""cherry"中的"apple" "banana" "cherry"

SDS长这样

free:还剩多少空间 len:字符串长度 buf:存放的字符数组

空间预分配

为减少修改字符串带来的内存重分配次数,sds采用了“一次管够”的策略:

  • 若修改之后sds长度小于1MB,则多分配现有len长度的空间
  • 若修改之后sds长度大于等于1MB,则扩充除了满足修改之后的长度外,额外多1MB空间

惰性空间释放

为避免缩短字符串时候的内存重分配操作,sds在数据减少时,并不立刻释放空间。

int

就是redis中存放的各种数字 包括以下这种,故意加引号“”的

双向链表

长这样:

分两部分,一部分是“统筹部分”:橘黄色,一部分是“具体实施方“:蓝色。

主体”统筹部分“:

  • head指向具体双向链表的头
  • tail指向具体双向链表的尾
  • len双向链表的长度

具体"实施方":一目了然的双向链表结构,有前驱 pre有后继 next

由 list和 listNode两个数据结构构成。

ziplist

压缩列表。redis的列表键和哈希键的底层实现之一。此数据结构是为了节约内存而开发的。和各种语言的数组类似,它是由连续的内存块组成的,这样一来,由于内存是连续的,就减少了很多内存碎片和指针的内存占用,进而节约了内存。

然后文中的 entry的结构是这样的:

元素的遍历

先找到列表尾部元素:

然后再根据ziplist节点元素中的 previous_entry_length属性,来逐个遍历:

连锁更新

再次看看 entry元素的结构,有一个 previous_entry_length字段,他的长度要么都是1个字节,要么都是5个字节:

  • 前一节点的长度小于254字节,则 previous_entry_length长度为1字节
  • 前一节点的长度大于254字节,则 previous_entry_length长度为5字节

假设现在存在一组压缩列表,长度都在250字节至253字节之间,突然新增一新节点 new, 长度大于等于254字节,会出现:

程序需要不断的对压缩列表进行空间重分配工作,直到结束。

除了增加操作,删除操作也有可能带来“连锁更新”。请看下图,ziplist中所有entry节点的长度都在250字节至253字节之间,big节点长度大于254字节,small节点小于254字节。

哈希表

哈希表略微有点复杂。哈希表的制作方法一般有两种,一种是:开放寻址法,一种是 拉链法。redis的哈希表的制作使用的是 拉链法。

整体结构如下图:

也是分为两部分:左边橘黄色部分和右边蓝色部分,同样,也是”统筹“和”实施“的关系。具体哈希表的实现,都是在蓝色部分实现的。先来看看蓝色部分:

这也分为左右两边“统筹”和“实施”的两部分。

右边部分很容易理解:就是通常拉链表实现的哈希表的样式;数组就是bucket,一般不同的key首先会定位到不同的bucket,若key重复,就用链表把冲突的key串起来。

新建key的过程:

假如重复了:

rehash

再来看看哈希表总体图中左边橘黄色的“统筹”部分,其中有两个关键的属性:ht和 rehashidx。ht是一个数组,有且只有俩元素ht[0]和ht[1];其中,ht[0]存放的是redis中使用的哈希表,而ht[1]和rehashidx和哈希表的 rehash有关。

rehash指的是重新计算键的哈希值和索引值,然后将键值对重排的过程。

加载因子(load factor)=ht[0].used/ht[0].size。

扩容和收缩标准

扩容:

  • 没有执行BGSAVE和BGREWRITEAOF指令的情况下,哈希表的 加载因子大于等于1。
  • 正在执行BGSAVE和BGREWRITEAOF指令的情况下,哈希表的 加载因子大于等于5。

收缩:

  • 加载因子小于0.1时,程序自动开始对哈希表进行收缩操作。

扩容和收缩的数量

扩容:

  • 第一个大于等于 ht[0].used*2的 2^n(2的n次方幂)。

收缩:

  • 第一个大于等于 ht[0].used的 2^n(2的n次方幂)。

(以下部分属于细节分析,可以跳过直接看扩容步骤)对于收缩,我当时陷入了疑虑:收缩标准是 加载因子小于0.1的时候,也就是说假如哈希表中有4个元素的话,哈希表的长度只要大于40,就会进行收缩,假如有一个长度大于40,但是存在的元素为4即( ht[0].used为4)的哈希表,进行收缩,那收缩后的值为多少?

我想了一下:按照前文所讲的内容,应该是4。但是,假如是4,存在和收缩后的长度相等,是不是又该扩容?翻开源码看看:

收缩具体函数:

由代码我们可以看到,假如收缩后长度为4,不仅不会收缩,甚至还会报错。()

我们回过头来再看看设定:题目可能成立吗?哈希表的扩容都是2倍增长的,最小是4, 4 ===》 8 ====》 16 =====》 32 ======》 64 ====》 128

也就是说:不存在长度为 40多的情况,只能是64。但是如果是64的话,64 X 0.1(收缩界限)= 6.4 ,也就是说在减少到6的时候,哈希表就会收缩,会缩小到多少呢?是8。此时,再继续减少到4,也不会再收缩了。所以,根本不存在一个长度大于40,但是存在的元素为4的哈希表的。

扩容步骤

收缩步骤

渐进式refresh

在"扩容步骤"和"收缩步骤" 两幅动图中每幅图的第四步骤“将ht[0]中的数据利用哈希函数重新计算,rehash到ht[1]”,并不是一步完成的,而是分成N多步,循序渐进的完成的。因为hash中有可能存放几千万甚至上亿个key,毕竟Redis中每个hash中可以存 2^32-1 键值对(40多亿),假如一次性将这些键值rehash的话,可能会导致服务器在一段时间内停止服务,毕竟哈希函数就得计算一阵子呢((#^.^#))。

哈希表的refresh是分多次、渐进式进行的。

渐进式refresh和下图中左边橘黄色的“统筹”部分中的 rehashidx密切相关:

  • rehashidx 的数值就是现在rehash的元素位置
  • rehashidx 等于 -1 的时候说明没有在进行refresh

甚至在进行期间,每次对哈希表的增删改查操作,除了正常执行之外,还会顺带将ht[0]哈希表相关键值对rehash到ht[1]。

以扩容步骤为例:

intset

整数集合是集合键的底层实现方式之一。

跳表

跳表这种数据结构长这样:

redis中把跳表抽象成如下所示:

看这个图,左边“统筹”,右边实现。统筹部分有以下几点说明:

  • header: 跳表表头
  • tail:跳表表尾
  • level:层数最大的那个节点的层数
  • length:跳表的长度

实现部分有以下几点说明:

  • 表头:是链表的哨兵节点,不记录主体数据。
  • 是个双向链表
  • 分值是有顺序的
  • o1、o2、o3是节点所保存的成员,是一个指针,可以指向一个SDS值。
  • 层级高度最高是32。每次创建一个新的节点的时候,程序都会随机生成一个介于1和32之间的值作为level数组的大小,这个大小就是“高度”

redis五种数据结构的实现

redis对象

redis中并没有直接使用以上所说的各种数据结构来实现键值数据库,而是基于一种对象,对象底层再间接的引用上文所说的具体的数据结构。

结构如下图:

字符串

其中:embstr和raw都是由SDS动态字符串构成的。唯一区别是:raw是分配内存的时候,redisobject和 sds 各分配一块内存,而embstr是redisobject和raw在一块儿内存中。

列表

hash

set

zset


http://chatgpt.dhexx.cn/article/dnS42OmV.shtml

相关文章

redis底层数据结构-List

举例分析 创建列表对象 numbers 列表对象有两种底层实现结构 1.压缩列表(zipList)实现的列表对象 压缩列表(zipList)是Redis为了节省内存而开发的,是由一系列特殊编码的连续内存块组成的顺序型数据结构,一个压缩列表可以包含任意多个节点(e…

redis中hash数据结构

目录 hash的数据结构ziplist底层实现字典底层实现扩容缩容 引用 hash的数据结构 hash底层数据结构的实现包括两种:ziplist和字典当保存的所有键值对字符串长度小于 64 字节并且键值对数量小于 512 时使用ziplist ,否则使用字典的方式 ziplist底层实现 …

redis的五种数据结构

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 目录 前言 一、字符串(String) 1.SDS的定义 2.SDS与C语言中字符串的区别(优点) 2.1 获取字符串长度 2.2 防止缓冲区的溢…

「Redis数据结构」哈希对象(Hash)

「Redis数据结构」哈希对象(Hash) 文章目录 「Redis数据结构」哈希对象(Hash)一、概述二、编码ZipListHashTable 三、编码转换 一、概述 Redis中hash对象是一个string类型的field和value的映射表,hash特别适合用于存储…

Redis底层数据结构(图文详解)

目录 前言 Redis为什么要使用2个对象?两个对象的好处 redisObject对象解析 String 类型 1、int 整数值实现 2、embstr 3、raw List 类型 1、压缩链表:ziplist 2、双向链表:linkedlist 3、快速列表:quicklist Hash …

Redis数据结构之hash

对象类数据的存储如果具有较频繁的更新需求操作会显得笨重,这里我们可以用redis的hash数据类型解决。 一、hash类型 新的存储需求:对一系列存储的数据进行编组,方便管理,典型应用存储对象信息 需要的存储结构:一个存储…

Redis数据结构之Zset

文章目录 一、zset数据结构二、跳表skipList什么是跳表?1.跳表的查找2.跳表的插入3.跳表的删除4.跳表的更新 一、zset数据结构 相比于set,sorted set 增加了一个权重参数 score,使得集合中的元素能够按 score 进行有序排列,还可以…

Redis数据结构有哪些

Redis数据结构有哪些 一、Redis数据结构 一、Redis数据结构 Redis是一种基于内存的数据库,并且提供一定的持久化功能,它是一种键值(key-value)数据库,使用 key 作为 索引找到当前缓存的数据,并且返回给程序…

「Redis数据结构」压缩列表(ZipList)

「Redis数据结构」压缩列表(ZipList) 文章目录 「Redis数据结构」压缩列表(ZipList)一、概述二、结构三、连锁更新问题四、压缩列表的缺陷五、小结参考 ZipList 是一种特殊的“双端链表” ,由一系列特殊编码的连续内存…

redis数据结构hash

Redis数据结构之hash Hash存储结构 Hash是一个string 类型的field和value的映射表。Hash特别适合存储对象,相对于将对象的每个字段存成单个string 类型。一个对象存储在Hash类型中会占用更少的内存,并且可以更方便的存取整个对象。 我们简单举个实例来…

简述redis数据结构

String:字符串 List:列表 Hash:哈希表 Set:无序集合 Sorted Set:有序集合 bitmap:布隆过滤器 GeoHash:坐标,借助Sorted Set实现,通过zset的score进行排序就可以得到坐标附…

Redis数据结构之——hash

写在前面 以下内容是基于Redis 6.2.6 版本整理总结 一、Redis 数据结构hash的编码格式 Redis中hash数据类型使用了两种编码格式:ziplist(压缩列表)、hashtable(哈希表) 在redis.conf配置文件中,有以下两个参数,意思为:当节点数量小于512并…

Redis底层数据结构详解

Redis底层数据结构详解 我们知道Redis常用的数据结构有五种,String、List、Hash、Set、ZSet,其他的集中数据结构基本上也是用这五种实现的,那么,这五种是Redis提供给你的数据结构,这五种数据结构式怎么实现的&#xf…

Redis的五种基础数据结构

Redis的五种基础数据结构 Redis有5种基础数据结构,分别为:String(字符串),list(列表),hash(字典),set(集合)和zset(有序集合)。 1.String(字符串) 字符串的结构 字符串String是Redis最简单的数据结构,它的内部表示…

Redis的数据结构

一、redis的数据结构 1、String字符串类型 Redis的String能够表示字符串、整数、浮点数三种值的类型 应用场景: 普通的赋值使用incr、decr命令进行递增和递减统计数据。用于实现乐观锁watch(事物)setNx实现分布式锁 底层数据类型: // 数据结构 stru…

为了拿捏 Redis 数据结构,我画了 40 张图(完整版)

大家好,我是小林。 Redis 为什么那么快? 除了它是内存数据库,使得所有的操作都在内存上进行之外,还有一个重要因素,它实现的数据结构,使得我们对数据进行增删查改操作时,Redis 能高效的处理。…

IDEA 配置Services面板

IDEA中的services窗口是一个管理所有服务的地方,特别是在使用spring cloud项目开发中,对多个微服务的管理非常实用。 配置方式一: 在IDEA中可通过配置将services窗口面板显示出来。 配置方式二: 找到项目.idea目录下worksp…

如何在IDEA配置Tomcat

1、点击RUN下拉的Edit Configurations 2、点击左上角号,下拉找到Tomcat Server,选择Local 3、 配置Tmcat信息 4、点击Deployment,选择右边的号,选择Artifict 5、Application context这里只写个“/”就可以了 6、点击Apply,然后返回…

idea配置代理服务器

1、查看代理服务器ip和端口 使用http代理 2、idea中配置代理服务器 选择File --> Settings --> 搜索HTTP Proxy --> 选择Manual proxy configuration --> 选择HTTP输入ip和端口 --> 点击Apply 3、配置jvm启动参数 -Djava.net.useSystemProxiestrue 完成!

IDEA配置安卓环境

IDEA是JetBrains公司推出的Java集成开发环境 ,这学期又开了安卓课,少不了安卓环境,不过android stdio是完全支持安卓的,不适合开发java项目,作为小白,我觉得IDEA一个软件就够了,当然&#xff0c…