type
status
date
slug
summary
tags
category
titleIcon
password
icon
calloutIcon
🎈
调试Redis源码 +《Redis 5设计与源码分析》阅读笔记

预备工作

参考书籍

notion image

编译Redis源码

  • 配置git代理与clone代码仓库
  • 先修改src目录下的Makefile去掉编译优化,便于后续源码调试查看中间变量值,否则可能因编译优化而无法查看部分变量值(vscode显示为optimized out)
  • 主要修改两点:1.OPTIMIZATION的O2改为O0 2.REDIS_LD后添加$(OPTIMIZATION)
  • 如果一开始没有修改就进行了编译,可以修改文件后通过make clean清除现有内容重新make
  • 如果移动过文件夹(比如从虚拟机本地文件夹移动到VMware的共享文件夹),也可以先make clean清除再重新make一次,不然调试找不到server.c的main函数
notion image
notion image
  • optimized out的情况展示,就是中间结果被优化了,需要关掉O2优化,开O0
notion image
  • 在项目目录下使用make
notion image
  • 在src目录下make install
notion image
  • 如果权限不够可以先修改对应目录的权限,非仅个人使用的机器慎用777
notion image
notion image
  • 理想效果:
先查看是否有其他的redis-server,比如自行安装的,或者明确指定使用以下目录的内容
notion image
redis-server启动效果
notion image
redis-benchmark效果
notion image

VSCode配置Redis调试环境

调试文件
在项目目录下创建.vscode文件夹,往里面放入两个文件
launch.json
tasks.json
在调试界面使用gcc任务,可以观察到进入server.c的main函数
notion image
SDS示范效果 initlen为8,从call stack可以看出,本次创建的是key,test_sds总长为8
notion image

数据结构

SDS

hdr - header 表示可用于存储对应长度的字符串对应存储长度与容量的位数

跳表

创建

确定层高
每个节点创建时
 
notion image
删除known_hosts
开启一个redis-server redis-cli
需要重新make clean 再 make
如果不行就重新下仓库,改完Makefile再make
刚进入main就应该能看到j = 0,如果是optimized out就是优化没改
notion image
notion image
 

压缩列表

第4章 压缩列表 压缩列表ziplist本质上就是一个字节数组,是Redis为了节约内存而 设计的一种线性数据结构,可以包含多个元素,每个元素可以是一个字 节数组或一个整数。 Redis的有序集合、散列和列表都直接或者间接使用了压缩列表。 当有序集合或散列表的元素个数比较少,且元素都是短字符串时, Redis便使用压缩列表作为其底层数据存储结构。列表使用快速链表 (quicklist)数据结构存储,而快速链表就是双向链表与压缩列表的组 合。 例如,使用如下命令创建一个散列键并查看其编码。
127.0.0.1:6379> hmset person name zhangsan gender 1 age 22 OK 127.0.0.1:6379> object encoding person "ziplist"
HEADER:zlbytes 4字节 uint32 + zltail uint32 + zllen uint16 所以不能存储超过(2^16 - 1)元素个数 最多允许的字节数不超过(2^32 - 1)
END:zlend 1字节 uint8 表示结尾 固定为255 即为0xFF
notion image
notion image
notion image
《宏定义成员方法》
TAIL_OFFSET尾元素相对于起始地址偏移量
notion image
notion image
了解了压缩列表的基本结构,我们可以很容易地获得压缩列表的字 节长度、元素个数等,那么如何遍历压缩列表呢?对于任意一个元素, 我们如何判断其存储的是什么类型呢?我们又如何获取字节数组的长度 呢? 回答这些问题之前,需要先了解压缩列表元素的编码结构
notion image
 
previous_entry_length字段表示前一个元素的字节长度,占1个或者5 个字节,当前一个元素的长度小于254字节时,用1个字节表示;当前一 个元素的长度大于或等于254字节时,用5个字节来表示。而此时 previous_entry_length字段的第1个字节是固定的0xFE,后面4个字节才真 正表示前一个元素的长度。假设已知当前元素的首地址为p,那么pprevious_entry_length就是前一个元素的首地址,从而实现压缩列表从尾 到头的遍历。 encoding字段表示当前元素的编码,即content字段存储的数据类型 (整数或者字节数组),数据内容存储在content字段。为了节约内存, encoding字段同样长度可变。压缩列表元素的编码如表4-1所示。
notion image
notion image
可以看出,根据encoding字段第1个字节的前2位,可以判断content 字段存储的是整数或者字节数组(及其最大长度)。当content存储的是 字节数组时,后续字节标识字节数组的实际长度;当content存储的是整 数时,可根据第3、第4位判断整数的具体类型。而当encoding字段标识 当前元素存储的是0~12的立即数时,数据直接存储在encoding字段的最 后4位,此时没有content字段。参照encoding字段的编码表格,Redis预 定义了以下常量对应encoding字段的各编码类型:
 
 
 
4.2 结构体 4.1节介绍了压缩列表的存储结构,我们发现对于压缩列表的任意 元素,获取前一个元素的长度、判断存储的数据类型、获取数据内容都 需要经过复杂的解码运算。解码后的结果应该被缓存起来,为此定义了 结构体zlentry,用于表示解码后的压缩列表元素。
 
结构体zlentry定义了7个字段,而4.1节显示每个元素只包含3个字 段。 回顾压缩列表元素的编码结构,可变因素实际上不止3个: previous_entry_length字段的长度(prevrawlensize)、 previous_entry_length字段存储的内容(prevrawlen)、encoding字段的长 度(lensize)、encoding字段的内容(len表示元素数据内容的长度, encoding表示数据类型)和当前元素首地址(p);而headersize则表示 当前元素的首部长度,即previous_entry_length字段长度与encoding字段 长度之和。 函数zipEntry用来解码压缩列表的元素,存储于zlentry结构体。
notion image
《没有输入类型与返回类型的函数》do { } while(0);
既使用高亮又避开注释格式限制
notion image
只有系统为big endian时候生效
notion image
 
notion image
 
notion image
 
notion image
每次指针操作先获取要读取的字段的字节/比特长度,然后决定1.按照涉及的范围读取内容2.跳到下一个字段的开头
 
 

哈希

1)可以存储海量数据,键值对是映射关系,可以根据键以O(1)的 时间复杂度取出或插入关联值。 2)键值对中键的类型可以是字符串、整型、浮点型等,且键是唯 一的。例如:执行set test"hello world"命令,此时的键test类型为字符 串,如test这个键存在数据库中,则为修改操作,否则为插入操作。 3)键值对中值的类型可为String、Hash、List、Set、SortedSet。
 
 
作用是把任意长度的输入通过散列算法转换成固定类型、固定长度的散列值,换句话 说,Hash函数可以把不同键转换成唯一的整型数据。散列函数一般拥有 如下特征: 1)相同的输入经Hash计算后得出相同输出; 2)不同的输入经Hash计算后一般得出不同输出值,但也可能会出现相同输出值。 所以,好的Hash算法是经过Hash计算后其输出值具有强随机分布性。例如Daniel J.Bernstein在comp.lang.c上发布的“times 33”散列函数, 其使用的核心算法是:“hash(i)=hash(i-1)*33+str[i]”,这是针对字符串已 知的最好的散列函数之一,因为其计算速度快,而且输出值分布得很好。 在应用上,通常使用现成的开源Hash算法,例如Redis自带客户端 就是使用“times 33”散列函数来计算字符串的Hash值,Redis服务端的 Hash函数使用的是siphash算法,主要功能与客户端Hash函数类似,其优 点是针对有规律的键计算出来的Hash值也具有强随机分布性,但算法较 为复杂
 
dictGenHashFunction函数主要作用是,入参是任意长度的字符串, 通过Hash计算后返回无符号整型数据。因此,我们可以通过Hash函数, 将任意输入的键转换成整型数据,使其可以当作数组的下标使用。 读到这里,想必有读者会有疑问,前文中字典的第2个特征是“键的类型可以为字符串、整型、浮点型等”,而Hash函数只把字符串转换成 整型数据,当遇到键的类型为非字符串时该如何处理?答案很简单,第 2个特征中键的类型是客户端感知的,而Redis服务端收到客户端发送过 来的键实际都为字符串。
当客户端执行“set 100.86 hello”命令时,此时的键在客户端看来是 浮点型数据,但Redis服务端收到的键的值其实就是字符串——100.86, 字符串长度为6,经过Hash函数转换后返回值为 11361771491584941503。
字典数据结构在这就需要添加第2及第3个字段, 分别为:①总容量——size字段;②已存入数据量——used字段
为了解决Hash冲突,所以数组中的元素除了应把键值对中的“值”存 储外,还应该存储“键”信息和一个next指针,next指针可以把冲突的键 值对串成单链表,“键”信息用于判断是否为当前要查找的键。此时数组 中元素的字段也明确了
notion image
人为设定Hash表的数组容量初始值为4,随着键值对存储 量的增加,就需对Hash表扩容,新扩容的容量大小设定为当前容量大小 的一倍,也就是说,Hash表的容量大小只能为4,8,16,32...。而sizemask 掩码的值就只能为3,7,15,31...,对应的二进制为11,111,1111,11111..., 因此掩码值的二进制肯定是每一位都为1。
索引值=Hash值&掩码值,对应Redis源码为:idx=hash&d>ht[table].sizemask,其计算结果等同Hash值与Hash表容量取余,而计算 机的位运算要比取余运算快很多。
notion image
notion image
dictht dictEntry dict
 
notion image
 
notion image
dictType结构体,则是为了实现各种形态的字典而抽象出来的一组操作函数。
 
 
 

·privdata字段,私有数据,配合type字段指向的函数一起使用。

·ht字段,是个大小为2的数组,该数组存储的元素类型为dictht,虽然有两个元素,但一般情况下只会使用ht[0],只有当该字典扩容、缩容需要进行rehash时,才会用到ht[1],rehash介绍详见5.3.2节。

·rehashidx字段,用来标记该字典是否在进行rehash,没进行rehash时,值为-1,否则,该值用来表示Hash表ht[0]执行rehash到了哪个元素,并记录该元素的数组下标值。

·iterators字段,用来记录当前运行的安全迭代器数,当有安全迭代器绑定到该字典时,会暂停rehash操作。Redis很多场景下都会用到迭代器,例如:执行keys命令会创建一个安全迭代器,此时iterators会加1,命令执行完毕则减1,而执行sort命令时会创建普通迭代器,该字段不会改变,关于迭代器的介绍详见5.4.1节。

 
notion image
在redis-server启动中,整个数据库会先初始化一个空的字典用于存储整个数据库的键值对。初始化一个空字典,调用的是dict.h文件中的dictCreate函数,对应源码为
 
 
原文给定的dictCreate定义错误
hiredis
 
 
notion image
 
 
唯一声明
notion image
 
 
不包括hires1个,有2个dictType变量声明,不使用DictType后缀抽出的常量
notion image
notion image
notion image
 
相关文章
MetingJS使用自定义音乐源-CF+Huggingface部署
Lazy loaded image
Win与linux开发环境配置|Powershell与Zsh配置记录
Lazy loaded image
折腾linux虚拟机杂记
Lazy loaded image
Leetcode Hot 100解题记录 - 草稿
Lazy loaded image
天机学堂完结复盘
Lazy loaded image
天机学堂完结复盘-更新草稿
Lazy loaded image
折腾linux虚拟机杂记Leetcode Hot 100解题记录 - 草稿
Loading...
CamelliaV
CamelliaV
Java;CV;ACGN
最新发布
SEU9系本硕资料
2025-6-12
Leetcode Hot 100解题记录 - 草稿
2025-6-12
神领物流Day02复盘-运费业务 - 草稿
2025-6-4
天机学堂完结复盘-更新草稿
2025-6-4
Redis5.0源码学习 - 草稿
2025-6-3
折腾linux虚拟机杂记
2025-6-3
公告
计划:
  • LLM相关
  • 支付业务 & 双token无感刷新
  • (线程池计算优惠方案)天机学堂Day09-Day12复盘-优惠劵业务
  • (业务复盘,技术汇总)天机学堂完结复盘
  • hot 100
 
2024-2025CamelliaV.

CamelliaV | Java;CV;ACGN