Redis【1】- 如何阅读Redis 源码
1 Redis 的简介
Redis 实际上是简称,全称为 Remote Dictionary Server
(远程字典服务器),由 Salvatore Sanfilippo 写的高性能 key-value 存储系统,其完全开源免费,遵守 BSD 协议。Redis 与其他 key-value 缓存产品(如 memcache)有以下几个特点。
- 数据持久化:可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
- 数据结构简单丰富:既有简单的 key-value 类型的数据,同时还提供 list,set,zset,hash 等数据结构的存储。
- 高可用:支持主从、哨兵、集群等模式,可以有效提高可用性。
Redis 也是一种 分布式缓存 [[1. 从缓存到分布式缓存]],其代码是 c 语言写的,那我们该如何阅读呢?
2 环境搭建
环境依赖,先看看 gcc 、cc、g++ 有没有安装
whereis gcc
whereis cc
whereis g++
安装gcc
xcode-select --install
brew install gcc
brew install pkg-config
查看 gcc 的版本:
$ gcc --version
Apple clang version 14.0.0 (clang-1400.0.29.202)
Target: x86_64-apple-darwin22.1.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
我使用 CLion 2022.3.1
,这个版本可以支持 Makefile
的项目,我们可以检查一下环境是不是有问题, 如果有问题,这里会有错误信息,我的之前报错是因为 Clion 的版本版本太低了,升级之后就好了。
下载Redis源码:
git clone https://github.com/redis/redis.git
切换到指定的版本
git checkout 7.0
File => New CMake Project from Sources
, 打开源码项目, 会自动生成根目录下的 CMakeList.txt
文件:
Clion 导入项目的时候选择已有的 MakeFile 文件,如果有是否 clean
项目,选择 clean
即可,之后可以点开 MakeFile
文件:
如果需要禁止编译器优化,可以使用下面命令:
make CFLAGS="-g -O0" MALLOC=jemalloc
运行完之后, Src 文件下就会出现可运行文件:
然后可以看到这些可运行的选项,继而配置Edit configuration
运行配置:
选择 debug
进行启动,启动成功,然后可以进行调试了:
可以使用 Redis Desktop Manager
来进行连接:
或者命令行连接(没有密码就可以不需要 -a 12345):
redis-cli -h 127.0.0.1 -p 6379 -a 12345
如果头文件引入报红色下划线,那就试试重新加载一下
3 Redis源码阅读技巧
3.1 Redis 的目录结构
Redis 的目录:
- deps: Redis 所依赖的第三方代码库
- hdr_histogram:用于生成命令的延迟追踪直方图
- hiredis:官方c语言客户端
- Jemalloc:内存分配器,默认情况下选择该内存分配器来代替 Linux 系统的 libc-malloc,libc-malloc 性能不高,且碎片化严重。
- linenoise:一种读线替换。它由 Redis 的 同一作者开发,但作为一个单独的项目进行管理,并根据需要进行更新。
- lua:lua 脚本相关的功能。
- src:源代码
- commons:都是 json 文件,放着每个指令的原信息。
- modules:实现 Redis Module 的示例代码。
- 其他文件均是源码
- test:测试代码
- cluster,Redis Cluster 功能测试。
- sentinel,哨兵集群功能测试。
- unit,单元测试。
- integration,主从复制功能测试。
- utils:工具类
- Makefile:编译文件
- redis.conf : redis 启动的配置文件
- sentinel.conf:哨兵配置
3.2 Redis 源码阅读顺序
网上的源码阅读顺序(引自网上):
- 自底向上:从耦合关系最小的模块开始读,然后逐渐过度到关系紧密的模块。就好像写程序的测试一样,先从单元测试开始,然后才到功能测试。
- 从功能入手:通过文件名(模块名)和函数名,快速定位到一个功能的具体实现,然后追踪整个实现的运作流程,从而了解该功能的实现方式。
- 自顶向下:从程序的 main() 函数,或者某个特别大的调用者函数为入口,以深度优先或者广度优先的方式阅读它的源码。
从大方向来说,学习 Redis 会有两种路径:
- 先从数据机构入手,直接手撕数据结构
- 好处:学着踏实,知根知底
- 坏处:容易从入门到放弃
- 先从启动 Redis 开始,跟着启动顺序读源码,跟着具体的操作读源码
- 好处:比较符合人的认知路线,知道 Redis 启动做了哪些操作,执行命令时做了哪些操作。
- 坏处:容易迷路,前期看哪一句,都不知道在干嘛,毕竟 RDB,AOF,集群,哨兵这些源码,如果实操过才相对容易理解一点。
个人建议是先学习如何启动 Redis,抓大放小(大致知道哪个类启动,读那些配置文件,大概是做什么用的),学习 Redis 到底能干什么,大致知道 Redis 的一些用法之后,再去了解 Redis 的常用的数据结构,到底怎么实现的,这个时候对 Redis 的一些数据结构大致有印象,之后可以跟着 Redis 启动,执行命令去看具体功能执行的路径。
在 Debug 的过程中,可以加深影响,更加了解数据结构的设计,代码的调用关系。
4 C语言的知识
4.1 #define的基本用法
在C语言中,常量是使用频率很高的一个量。常量是指在程序运行过程中,其值不能被改变的量。常量常使用 #define
来定义。
使用#define
定义的常量也称为符号常量,可以提高程序的运行效率,Redis 的源代码中有比较多的地方都使用该方式。
一般有以下两种用法:
#define 宏名 宏值
#define 宏名(参数列表) 表达式
第一种就是定义常量,比如:
#define N 100
此后直到 #undef N
之前, N的值都是100。当遇到#undef N
,其后如果再出现 N,则 N 需要重新定义之后才可以使用。
第二种语法常用来定义符号函数。
例如:
#define AREA(x,y) (x)*(y)
表示用来求长和宽分别是x和y的矩形的面积。
需要注意的是,在表达式(x) * (y)中,x和y都要使用“()”括起来,这是因为符号函数在编译时时进行符号形式替换。如果不加()则可能会发生意想不到的错误,例如:
#define AREA(x,y) x*y
...
A = AREA( 2+3, 1+2 );
此处预期的结果是15,但是实际的结果却是7,这是因为该段代码在编译进行了简单的符号替换而得到的实际表达式是:
A = 2+3 * 1+2;
根据运算符的优先级,先进行乘法运算,然后才是加法,这就导致了错误。
而如果使用
#define AREA(x,y) (x)*(y)
...
A = AREA( 2+3, 1+2 );
则在编译时替换的结果是:
A = (2+3) * (1+2);
#include"stdio.h"
#define AREA(x,y) (x)*(y)
int main()
{ int a = AREA(2+3, 1+2); printf( " %d\n", a); return 0;
}
4.2 头文件
Redis 是使用 c 语言写的,里面有很多头文件:
#include "server.h"
#include "monotonic.h"
#include "cluster.h"
#include "slowlog.h"
#include "bio.h"
#include "latency.h"
#include "atomicvar.h"
#include "mt19937-64.h"
#include "functions.h"
#include "syscheck.h" #include <time.h>
以 <
开头的,比如 #include <time.h>
是标准库的头文件,会在系统指定路径下查找,对应到 Java
里面可以理解为 官方的 jdk 里面的类,而类似 #include "server.h"
则是工程里面自定义的。
我没怎么写过 c 语言的代码, 一般 .c
文件是写实现的代码逻辑的,那如何在 a 文件里面写一个方法,让 b 文件也能用呢?
通过头文件的机制,类似 Java 里面的 接口, public
和 private
的概念,Java 中 一般希望对外暴露的方法,会设置为 public
,,如果不希望暴露,则设置为private
。c 语言里面如果希望暴露,则可以在头文件里面定义,否则不用定义。(虽然c语言是面向过程的,但是Redis确实在里面实践一些面向对象的思想)。
比如计算两数之和 与 两数之差 的乘积 test.c
long long mul(int a,int b) { return a*b;
} long long calculate(int a,int b) { return mul(a+b,a-b);
}
暴露出去的头文件test.h
long long calculate(int a,int b);
运行的代码 main.c
,可以正常计算结果为 -3
:
#include "stdio.h"
#include "test.h"
int main(){ printf("结果:%lld",calculate(1,2)); return 0;
}
但是如果直接引用 sum()
方法,则会报错,无法使用:
如果我们多次引用头文件会怎么样?结果是正常运行:
4.3 ifndef
Redis 里面有挺多的地方定义头文件的时候总是来一句 #isdef
或者 ifndef
#ifdef __linux__
#include <sys/mman.h>
#endif
#ifndef __ADLIST_H__
#define __ADLIST_H__
...
#endif /* __ADLIST_H__ */
如果加了 #ifndef
,则会判断只有没有定义这个宏的时候,才会定义它,第二次再次遇到 include
的时候,发现这个宏已经被定义过了,就会直接跳过,这样可以保证多次 include
也不会被解析多次,有且只有一次。
解析多次的坏处是什么?
- 如果在
.h
文件里面定义了全局变量,会导致变量重复定义。这个基本不太会,公司编码规范一般都会禁止,这样写是不人道的。 - 浪费编译时间。
既然禁止了在 .h
文件里面定义全局变量,那全局变量在哪里定义呢?当然是 .c
文件,比如 Redis 里面的全局变量:
那其他的文件怎么使用?这个 sever
可是全局唯一的,维护了 redis
的全部状态数据,那当然是暴露出去,在哪里暴露出去,在 .h
文件,使用关键字 extern
5 小结一下
阅读源码,是一件长期的事情,但是我们每次跟读代码的时候,一定要带着问题去阅读,否则效率会下降挺多。前期了解数据结构模型的时候,可以在网上找一些简单易懂的博客,最好是有图片的,书籍比较推荐《Redis 设计与实现》。有一定了解之后,会有些疑问,不用担心,此时再通过读源代码去验证我们的想法,可能不少小伙伴没学过 c 语言,也不必担心,语言之间都是相通的,其次即使有关键字不会,可以通过搜索也可以快速了解其作用。
希望我们都能从全局看功能 --> 实践 --> 抓大放小 --> 带疑问看源码 --> 重构知识图谱 --> 关联知识 --> 跳出细节俯瞰全局,最终完成 Redis 相关的知识学习,并形成一套自己的方法论。
作者:秦怀
相关文章:

Redis【1】- 如何阅读Redis 源码
1 Redis 的简介 Redis 实际上是简称,全称为 Remote Dictionary Server (远程字典服务器),由 Salvatore Sanfilippo 写的高性能 key-value 存储系统,其完全开源免费,遵守 BSD 协议。Redis 与其他 key-value 缓存产品(如…...
shell查看服务器的内存和CPU,实时使用情况
要查看服务器的内存和 CPU 实时使用情况,可以使用以下方法和命令: 1. 使用 top 运行 top 命令以显示实时的系统性能信息,包括 CPU 和内存使用情况。 top按 q 退出。输出内容包括: CPU 使用率:位于顶部,标…...

软件/游戏提示:mfc42u.dll没有被指定在windows上运行如何解决?多种有效解决方法汇总分享
遇到“mfc42u.dll 没有被指定在 Windows 上运行”的错误提示,通常是因为系统缺少必要的运行库文件或文件损坏。以下是多种有效的解决方法,可以帮助你解决这个问题: 原因分析 出现这个错误的原因是Windows无法找到或加载MFC42u.dll文件。这可…...

《Python基础》之函数、模块与库
目录 简介 一、函数 1、数学类函数 2、聚合类函数 3、和进制相关的函数 4、字符类函数 5、类型转换相关函数 6、获取输出类函数 二、模块与库的使用方法 1、模块和库的导入方法 2、第三方模块的下载 下载方法 简介 在Python编程的世界中,函数、模块和库是…...

selinux和防火墙实验
1 、 selinux 的说明 SELinux 是 Security-Enhanced Linux 的缩写,意思是安全强化的 linux 。 SELinux 主要由美国国家安全局( NSA )开发,当初开发的目的是为了避免资源的误用。 系统资源都是通过程序进行访问的,如…...

k8s Init:ImagePullBackOff 的解决方法
kubectl describe po (pod名字) -n kube-system 可查看pod所在的节点信息 例如: kubectl describe po calico-node-2lcxx -n kube-system 执行拉取前先把用到的节点的源换了 sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <<-EOF {"re…...

Spring AOP相关知识详解
难 文章目录 1.AOP介绍1.1 面向切面编程 - Aspect Oriented Programming (AOP)1.2 优点 2.AOP的概念2.1 连接点、切入点、通知、切面:2.2 注解2.2.1 通知类型2.2.1.1 通知的优先级排序 2.2.2 其他重要注解2.2.3 示例代码(四种通知) 3.Spring …...
selinux和防火墙
第七章 selinux 一、selinux的说明 SELinux:安全强化的 linux,Security-Enhanced Linux的缩写 SELinux : 由美国国家安全局( NSA )开发,目的是为了避免资源的误用 SELinux: 是对程序、文件等权…...

【vue for beginner】Composition API 和 Options API 的区别
🌈Don’t worry , just coding! 内耗与overthinking只会削弱你的精力,虚度你的光阴,每天迈出一小步,回头时发现已经走了很远。 📗概念 vue2中的方式叫Options API ,vue3中叫Composition API。 Composition…...

jmeter5.6.3安装教程
一、官网下载 需要提前配置好jdk的环境变量 jmeter官网:https://jmeter.apache.org/download_jmeter.cgi 选择点击二进制的zip文件 下载成功后,默认解压下一步,更改安装路径就行(我安装在D盘) 实用jmeter的bin目录作为系统变量 然后把这…...
关于Spring基础了解
Spring简介 Spring框架是一个开源的Java应用框架,旨在简化企业级应用程序的开发。它提供了一系列强大的工具和服务,帮助开发者构建高质量的Java应用程序。Spring框架的核心理念是使开发过程更加模块化、可测试和可维护。 主要特性 依赖注入(…...

输入json 达到预览效果
下载 npm i vue-json-pretty2.4.0 <template><div class"newBranchesDialog"><t-base-dialogv-if"addDialogShow"title"Json数据配置"closeDialog"closeDialog":dialogVisible"addDialogShow":center"…...

DataLoade类与list ,iterator ,yield的用法
1 问题 探索DataLoader的属性,方法 Vscode中图标含意 list 与 iterator 的区别,尤其yield的用法 2 方法 知乎搜索DataLoader的属性,方法 pytorch基础的dataloader类是 from torch.utils.data.dataloader import Dataloader 其主要的参数如下&…...
model_selection.train_test_split函数介绍
目录 model_selection.train_test_split函数实战 model_selection.train_test_split函数 model_selection.train_test_split 是 Scikit-Learn 中用于将数据集拆分为训练集和测试集的函数。这个函数非常有用,因为在机器学习中,我们通常需要将数据集分为训…...
Springboot 读取 resource 目录下的Excel文件并下载
代码示例: GetMapping("/download") public void download(HttpServletResponse response) {try {String filename "测试.xls";OutputStream outputStream response.getOutputStream();// 获取springboot resource 路径下的文件InputStream inputStream…...
SQL EXISTS 子句的深入解析
SQL EXISTS 子句的深入解析 引言 SQL(Structured Query Language)作为一种强大的数据库查询语言,广泛应用于各种数据库管理系统中。在SQL查询中,EXISTS子句是一种非常实用的工具,用于检查子查询中是否存在至少一行数…...
33.Java冒泡排序
冒泡排序: 一种排序的方式,对要进行排序的数据中相邻的数据进行两两比较,将较大的数据放在后面,依次对所有的数据进行操作,直至所有数据按要求完成排序. package Javase;import sun.security.util.ByteArrayTagOrder…...
Docker容器ping不通外网问题排查及解决
Docker容器ping不通外网问题排查及解决 解决方案在最下面,不看过程的可直接拉到最下面。 一台虚拟机里突然遇到docker容器一直访问外网失败,网上看到这个解决方案,这边记录一下。 首先需要明确docker的网桥模式,网桥工作在二层…...
JavaScript 库 number-precision 如何使用?
number-precision 是一个 JavaScript 库,主要用于处理 JavaScript 中的数字精度问题。它提供了一些方法,帮助你进行数字运算时保持精度,尤其是在涉及到浮点数运算时,它能够避免传统 JavaScript 中精度丢失的问题。 例如ÿ…...
faiss库中ivf-sq(ScalarQuantizer,标量量化)代码解读-2
文件ScalarQuantizer.h 主要介绍这里面的枚举以及一些函数内容:QuantizerType、RangeStat、ScalarQuantizer、train、compute_codes、decode、SQuantizer、FlatCodesDistanceComputer、get_distance_computer、select_InvertedListScanner QuantizerType 量化类型…...
后进先出(LIFO)详解
LIFO 是 Last In, First Out 的缩写,中文译为后进先出。这是一种数据结构的工作原则,类似于一摞盘子或一叠书本: 最后放进去的元素最先出来 -想象往筒状容器里放盘子: (1)你放进的最后一个盘子(…...

大数据学习栈记——Neo4j的安装与使用
本文介绍图数据库Neofj的安装与使用,操作系统:Ubuntu24.04,Neofj版本:2025.04.0。 Apt安装 Neofj可以进行官网安装:Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...

23-Oracle 23 ai 区块链表(Blockchain Table)
小伙伴有没有在金融强合规的领域中遇见,必须要保持数据不可变,管理员都无法修改和留痕的要求。比如医疗的电子病历中,影像检查检验结果不可篡改行的,药品追溯过程中数据只可插入无法删除的特性需求;登录日志、修改日志…...

基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容
基于 UniApp + WebSocket实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...
土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等
🔍 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术,可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势,还能有效评价重大生态工程…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)
漏洞概览 漏洞名称:Apache Flink REST API 任意文件读取漏洞CVE编号:CVE-2020-17519CVSS评分:7.5影响版本:Apache Flink 1.11.0、1.11.1、1.11.2修复版本:≥ 1.11.3 或 ≥ 1.12.0漏洞类型:路径遍历&#x…...

基于Springboot+Vue的办公管理系统
角色: 管理员、员工 技术: 后端: SpringBoot, Vue2, MySQL, Mybatis-Plus 前端: Vue2, Element-UI, Axios, Echarts, Vue-Router 核心功能: 该办公管理系统是一个综合性的企业内部管理平台,旨在提升企业运营效率和员工管理水…...
uniapp 字符包含的相关方法
在uniapp中,如果你想检查一个字符串是否包含另一个子字符串,你可以使用JavaScript中的includes()方法或者indexOf()方法。这两种方法都可以达到目的,但它们在处理方式和返回值上有所不同。 使用includes()方法 includes()方法用于判断一个字…...
绕过 Xcode?使用 Appuploader和主流工具实现 iOS 上架自动化
iOS 应用的发布流程一直是开发链路中最“苹果味”的环节:强依赖 Xcode、必须使用 macOS、各种证书和描述文件配置……对很多跨平台开发者来说,这一套流程并不友好。 特别是当你的项目主要在 Windows 或 Linux 下开发(例如 Flutter、React Na…...

AxureRP-Pro-Beta-Setup_114413.exe (6.0.0.2887)
Name:3ddown Serial:FiCGEezgdGoYILo8U/2MFyCWj0jZoJc/sziRRj2/ENvtEq7w1RH97k5MWctqVHA 注册用户名:Axure 序列号:8t3Yk/zu4cX601/seX6wBZgYRVj/lkC2PICCdO4sFKCCLx8mcCnccoylVb40lP...