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 量化类型…...
性能测试工具Grafana、InfluxDB和Collectd的搭建
一、性能监控组成简介 1、监控能力分工:这个系统组合能够覆盖从数据采集、存储到可视化的整个监控流程。Collectd可以收集各种系统和应用的性能指标,InfluxDB提供高效的时序数据存储,而 Grafana 则将这些数据以直观的方式呈现出来。2,实时性能监控:对于需要实时了解系统状…...
【ruby on rails】dup、deep_dup、clone的区别
一、区别 dup 浅复制:dup 方法创建对象的浅复制。 不复制冻结状态:dup 不会复制对象的冻结状态。 不复制单例方法:dup 不会复制对象的单例方法。 deep_dup 深复制:deep_dup 方法创建对象的深复制,递归复制嵌套的对象。…...
原生微信小程序画表格
wxml部分: <view class"table__scroll__view"><view class"table__header"><view class"table__header__item" wx:for"{{TableHeadtitle}}" wx:key"index">{{item.title}}</view></…...
Python实现IP代理池
文章目录 Python实现IP代理池一、引言二、步骤一:获取代理IP1、第一步:爬取代理IP2、第二步:验证代理IP的有效性 三、步骤二:构建IP代理池四、使用示例1、完整的使用示例2、注意事项3、处理网络问题 五、总结 Python实现IP代理池 …...
互联网直播/点播EasyDSS视频推拉流平台视频点播有哪些技术特点?
在数字化时代,视频点播应用已经成为我们生活中不可或缺的一部分。监控技术与视频点播的结合正悄然改变着我们获取和享受媒体内容的方式。这一变革不仅体现在技术层面的进步,更深刻地影响了我们。 EasyDSS视频直播点播平台是一款高性能流媒体服务软件。E…...
32.4 prometheus存储磁盘数据结构和存储参数
本节重点介绍 : prometheus存储磁盘数据结构介绍 indexchunkshead chunksTombstoneswal prometheus对block进行定时压实 compactprometheus 查看支持的存储参数 prometheus存储示意图 内存和disk之间的纽带 wal WAL目录中包含了多个连续编号的且大小为128M的文件,…...
C7.【C++ Cont】范围for的使用和auto关键字
目录 1.知识回顾 2.范围for 格式 使用 运行结果 运行过程 范围for的本意 作用 注意 3.底层分析范围for的执行过程 反汇编代码 分析 4.auto关键字 格式 基本用法 在范围for中使用auto 1.知识回顾 for循环的使用参见25.【C语言】循环结构之for文章 2.范围for C…...
联通云服务器部署老项目tomcat记录
1.先在服务器上安装mysql和tomcat 2.tomcat修改端口 3.在联通云运控平台配置tomcat访问端口(相当于向外部提供可访问端口) 4.将tomcat项目放在服务器tomcat的webapps里面 5.在mysql里创建项目数据库,运行sql创建表和导入数据 6.在配置文…...
剪映自动批量替换视频、图片素材教程,视频批量复刻、混剪裂变等功能介绍
一、三种批量替换模式的区别 二、混剪裂变替换素材 三、分区混剪裂变替换素材 四、按组精确替换素材 五、绿色按钮教程 (一)如何附加音频和srt字幕 (二)如何替换固定文本的内容和样式 (三)如何附加…...
el-dialog中调用resetFields()方法重置表单报错
前言 在开发中,弹框和表单是两个常见的组件,它们通常一起使用以实现用户交互和数据输入。然而,当我们尝试在弹框中调用表单的 resetFields() 方法时,有时会遇到报错的情况。 一、用法错误 确保 this.$refs[ruleForm].resetFields…...
企业网站seo关键词优化排名思路/手机网站排名优化软件
1 [System.Serializable] 使自定义的类能进行序列化,即当做一个public成员的时候可以在Inspector显示。 [SerializeField] 在Inspector版面中显示非public属性,并且序列化;若写在public前面,等于没写 2 [NonSerialized]在Insp…...
做自己的网站要多久/seo教程免费分享
本篇博客,先讲述同步和异步的区别,然后分别演示了同步和异步的基本范例程序;在这个过程中可以体味同步和异步的区别,以便按需选用;;;具体同步和异步的深入理解,需要在实际应用中逐渐…...
wordpress日志元代码/新东方考研培训机构官网
本系列文章是把作者刚接触和学习Linux时候的实操记录分享出来,内容主要包括Linux入门的一些理论概念知识、Web程序、mysql数据库的简单安装部署,希望能够帮到一些初学者,少走一些弯路。 注意: Linux下区分大小写; Linu…...
网站中的游戏是怎么做的/关键词百度网盘
1.业务代码 实体类(存放接口的发送信息) public class MailVO {/*** 邮件Id*/ApiModelProperty(value "邮件Id", name "邮件Id")private String id;/*** 邮件发送人*/ApiModelProperty(value "邮件发送人", name &q…...
棋牌网站开发多少钱/免费加客源软件
添加链接描述 数论: 三种情况: 1.找到最大的数,如果这个数是<0,说明都是负数或0,找到最大的负奇数既答案 2.最大的正数和为奇数,为答案, 3.最大的正数和为偶数,判断减去最小正奇数…...
淄博网站建设讲解透彻/济南今日头条最新消息
CSS规范 - 分类方法 SS文件的分类和引用顺序 通常,一个项目我们只引用一个CSS,但是对于较大的项目,我们需要把CSS文件进行分类。 我们按照CSS的性质和用途,将CSS文件分成“公共型样式”、“特殊型样式”、“皮肤型样式”ÿ…...