读懂 Redis 源码,我总结了这7点心得

76077b7d26ed3f63718761f0c071dad4.gif

作者|Magic Kaito

来源|水滴与银弹

阅读本文大约需要 8 分钟。

你好,我是 Kaito。

用了这么久的 Redis,也翻了很多次源码,经常有人问我到底怎么读 Redis 源码。

一提到读源码,很多人都会比较畏惧,认为读源码是高手才会做的事情。他们可能遇到问题时,会更倾向于去找别人分享的答案。但往往很多时候,自己查到的资料并不能解决所有问题,尤其是比较细节的问题。

从我的实战经验来看,遇到这种情况,通常就需要去源码中寻找答案了,因为在源码面前,这些细节会变得「一览无余」。

而且我认为,掌握读源码的能力,是从只懂得如何使用 Redis,到精通 Redis 实现原理的成长之路上,必须跨越的门槛。

可是,面对庞大复杂的项目,我们怎样读源码才能更高效呢?

这篇文章我就来和你聊一聊,我读 Redis 源码的经验,以及读源码的「通用思路」,希望这些心得可以帮助到你。

c767a59f0669bc318455a4b888230a01.png

01 找到地图

很多开源项目的源码,代码量一般都比较庞大,如果在读代码之前,我们没有制定合理的方法,就一头扎进去读代码,势必会把自己搞晕。

所以,我在拿到一个项目的代码之后,并不会马上着手去读,而是会先对整个项目结构进行梳理,划分出项目具体包含的模块。这样,我就对整个项目有了一个「宏观」的了解。

读代码就好比去一个陌生城市旅行,这个旅途过程充满着未知。如果在出发之前,我们手里能有一张地图,那我们对自己的行程就可以有一个非常清晰的规划。

我们就知道,如果想要到达目的地,需要从哪里出发、经过哪些地方、通过什么方式才能到达,有了地图就有了行进方向,否则很容易迷失。

因此,提前花一些时间梳理整个项目的「结构和目录」,对于后面更好地阅读代码是非常有必要的。

就拿 Redis 来举例,在读 Redis 源码之前,我们可以先梳理出整个项目的功能模块,以及每个模块对应的代码文件(src 下的代码结构):

0178cdf77b7a352fe54ce7cba84094a1.png

这样,有了这张地图之后,我们再去看代码的时候,就可以有重点地阅读了。

02 前置知识准备

在梳理完整个项目结构之后,我们就可以正式进入阅读环节当中了。不过,在阅读代码之前,我们其实还需要预先掌握一些「前置知识」。

因为一个完整的项目,必然综合了各个领域的技术知识点,比如数据结构、操作系统、网络协议、编程语言等,如果我们提前做好一些功课,在读源码的过程中就会轻松很多。

以下是根据我在阅读 Redis 书籍和实战过程中,提取的读源码必备前置知识点,你可以参考下:

  • 常用数据结构:数组、链表、哈希表、跳表

  • 网络协议:TCP 协议

  • 网络 IO 模型:IO 多路复用、非阻塞 IO、Reactor 网络模型

  • 操作系统:写时复制(Copy On Write)、常见系统调用、磁盘 IO 机制

  • C 语言基础:循环、分支、结构体、指针

当然,在阅读源码的过程中,我们也可以根据实际问题再去查阅相关资料,但不管怎样,提前熟悉这些方面的知识,在真正读代码时就会省下不少时间。

03 从基础模块开始读

好,有了地图并掌握了前置知识之后,接下来我们就要进入主题了:读代码

但具体要从哪个地方开始读起呢?我认为要先从「最基础」的模块开始读起。

我在前面也分析了,一个完整的项目会划分很多的功能模块,但这些模块并不是孤立的,而很可能是有「依赖」关系的。

比如说,Redis 中的 networking.c 文件,表示处理网络 IO 的具体实现。而如果我们能在理解事件驱动模块 ae.c 的基础上,再去阅读网络 IO 模块,效率就会更高。

那在 Redis 源码中,哪些是最基础的模块呢?

想一下,我们在使用 Redis 时,接触最频繁的是哪些功能?

答案是各种数据类型

一切操作的基础,其实都是基于这些最常用的数据类型来做的,比如 String、List、Hash、Set、Sorted Set等。所以,我们就可以从这些基础模块开始读起,也就是从 t_string.c、t_list.c、t_hash.c、t_set.c、t_zset.c 代码入手。

如果你对 Redis 的数据类型有所了解,就会看到这些数据类型在实现时,底层都对应了不同的数据结构。比如,String 的底层是 SDS,List 的底层是 ziplist + quicklist,Hash 底层可能是ziplist,也可能是哈希表,等等。

a7a4036d2fe304acf8f2f50d4cfeb167.png

由此一来,我们会发现,这些数据结构又是更为「底层」的模块,所以我们在阅读数据类型模块时,就需要重点聚焦在这些模块上,也就是 sds.c、ziplist.c、quicklist.c、dict.c、intset.c 文件,而且这些文件都是比较独立的,阅读起来就可以更加集中。

这样,当我们真正掌握了这些「底层数据结构」的实现后,就能更好地理解基于它们实现的各种「数据类型」了。

这些基础模块就相当于一座大厦的地基,地基打好了,才能做到高楼耸立。

04 找到核心主线

接着,掌握了数据结构模块之后,这时我们的重点就需要放在「核心主线」上来了。

在这个阶段,我们需要找到一个明确的目标,以这个目标为主线去读代码。因为读源码一个很常见的需求,就是为了了解这个项目最「核心功能」的实现细节,我们只有以此为目标,找到这条主线去读代码,才能达到最终目的。

那么在读 Redis 源码时,什么才是它的核心主线呢?这里我分享一个非常好用的技巧,就是根据「Redis 究竟是怎么处理客户端发来的命令的?」 为主线来梳理。

举个例子,当我们在执行 SET testkey testval EX 60 这样一条命令时,就需要搞清楚 Redis 是怎么执行这条命令的。

也就是要明确,Redis 从收到客户端请求,到把数据存到 Redis 中、设置过期时间,最后把响应结果返回给客户端,整个过程的每一个环节,到底是如何处理的。

有了这条主线,我们就有了非常明确的目标,而且沿着这条主线去读代码,我们还可以很清晰地把多个模块「串联」起来。比如从前面的例子中,我们会看到一条命令的执行,主要包含了这样几个阶段。

  • Redis Server 初始化:加载配置、监听端口、注册连接建立事件、启动事件循环(server.c、anet.c)。

  • 接收、解析客户端请求:初始化 client、注册读事件、读客户端 socket(networking.c)。

  • 处理具体的命令:找到对应的命令函数、执行命令(server.c、t_string.c、t_list.c、t_hash.c、t_set.c、t_zset.c)。

  • 返回响应给客户端:写客户端缓冲区、注册写事件、写客户端 socket(networking.c)。

dd2abec61e50f36035d7603c6b72bbb8.png

沿着这条主线去读代码,我们就可以掌握一条命令的执行全过程。

而且,由于这条主线的代码逻辑,已经覆盖了「所有命令」的执行流程,我们下次再去读其它命令时,比如 SADD,就只需要关注「处理命令」部分的逻辑即可,其它逻辑有 80% 都是相同的。

05 先整体后细节

当然,在阅读主线代码的过程中,肯定也会遇到过于「复杂」的函数,第一次在读这种函数时,很容易就会「陷进去」,导致整个主线代码的阅读,无法继续推进下去。

遇到这种情况其实是很正常的,可这时我们应该怎么办呢?

这里我的做法是,前期读到这种逻辑时,不要马上陷入到细节中去,而是要先「抓整体」。

具体来说,对于复杂的函数逻辑,我们刚开始并不需要知道它的每一个细节是如何实现的,而是只需知道这个函数「大致」做了几件事情即可。

举个例子,在执行 HSET 命令时,有一段代码很复杂,其中包括了很多分支判断,一次很难读懂:

0c841654718fbbe20f0e1bf056e60c76.png

那么,我在读这段代码时,就可以先简化逻辑,把握整体思路:

9476b3d856fc0784d5c8b9f751b2cc93.png

之后,再了解每个分支大致做了哪些事情:

ddbcabb079d36206fcb55d5b0bdbbc8d.png

这样做的好处,一是不会被复杂的细节逻辑搞晕,打击自己的自信心,二是可以有效避免阅读的连贯性被打断,从而能持续推进我们把整个主线逻辑读完。

所以,这里的重点就是:先把复杂代码的主逻辑搞清楚,知道涉及的每个方法完成了什么事,心里要先搭建一个简单的「框架」,等有了框架之后,我们再去给框架填充「细节」。

这样通过「先整体后细节」的方式,我们就可以不再畏惧代码中的复杂逻辑。

06 先主线后支线

不过,在阅读主线代码的过程中,我们肯定还会遇到各种「支线」逻辑,比如数据过期、替换淘汰、持久化、主从复制等。

其实,在阅读主线逻辑的时候,我们并不需要去重点关注这些支线,而当整个主线逻辑「清晰」起来之后,我们再去读这些支线模块,就会容易很多了。

这时,我们就可以从这些支线中,选取下一个「目标」,带着这个目标去阅读,比如说:

  • 过期策略是怎么实现的?(expire.c、lazyfree.c)

  • 淘汰策略是如何实现的?(evict.c)

  • 持久化 RDB、AOF 是怎么做的?(rdb.c、aof.c)

  • 主从复制是怎么做的?(replication.c)

  • 哨兵如何完成故障自动切换?(sentinel.c)

  • 分片逻辑如何实现?(cluster.c)

  • ...

有了新的支线目标后,我们依旧可以采用前面提到的「先整体后细节」的思路阅读相关模块,这样下来,整个项目的每个模块,就可以被「逐一击破」了。

07 查漏补缺

最后,我们还需要「查漏补缺」。

按照前面提到的方法,基本就可以把整个项目的主要模块读得七七八八了,这时我们基本已经对整个项目有了整体的「把控」。

不过,当我们在工作中遇到问题时,很有可能会发现,在当时读代码的过程中,有很多并不在意的「细节」被忽略了。

所以这时,我们就可以再带着「具体问题」出发,聚焦这个问题相关的模块,再一次去读源码。这样一来,我们就可以填补当时阅读源码的「空白区」。

举个例子,当我们在阅读 String 底层数据结构 SDS(简单动态字符串)的实现时,我们会看到当 SDS 需要追加新内容时会进行扩容,而我们之前阅读这块代码时,很有可能只是了解到有这样的逻辑存在,但并没有在意扩容的相关细节(一次扩容多大)。

所以,当我们在工作中遇到这个细节问题后,就可以把目光聚焦在 SDS 的扩容逻辑上(sds.c 的sdsMakeRoomFor函数),而此时我们会发现,当需要申请的新内存小于 1MB 时,Redis 就会翻倍申请内存,否则按 1MB 申请新内存。

采用这个方法进行查漏补缺,我们就可以对整个项目了解得更深入、更全面,真正把项目「吃透」。

总结

好了,以上就是我在阅读 Redis 源码时的经验心得,总结一下这 7 个步骤。

1、找到地图:拿到项目代码后,提前梳理整个项目结构,知晓整个项目的模块划分,以及对应的代码文件。

2、前置知识准备:提前掌握项目中用到的前置知识,比如数据结构、操作系统原理、网络协议、网络 IO 模型、编程语言语法等等。

3、从基础模块开始读:从最底层的基础模块开始入手,先掌握了这些模块,之后基于它们构建的模块读起来会更加高效。

4、找到核心主线:找到整个项目中最核心的主线逻辑,以此为目标,了解各模块为了完成这个功能,是如何协作和组织的。

5、先整体后细节:对于复杂函数,不要上来就陷入细节,前期阅读只需了解这个函数大致做了什么事情,建立框架,等搭建起框架之后,再去填充细节。

6、先主线后支线:整个主线逻辑清晰之后,再去延伸阅读支线逻辑,因为支线逻辑肯定是服务主线逻辑的,读完主线后再去读这些支线,也会变得更简单。

7、查漏补缺:在工作中遇到具体问题,带着这些实际的问题出发再次去读源码,进行查漏补缺,填补之前读源码时没有注意到的地方。

14bbcab578146ded8369aac7847454b7.gif

8f0ec055747b6252b4acccdfb1ddb44c.png

往期推荐

“5G+AI”到底有啥用?

云原生时代,底层性能如何调优?

到底什么是“无源物联网”?

张一鸣购得元宇宙入场券,谁将是头号玩家?

06f4f5bccf7cf0f2f2c297120809e9b3.gif

点分享

3a89831b2d596953761e305227aa4bfe.gif

点收藏

8d5fa9a44c67f032bd03890fd34b24da.gif

点点赞

d7993f6635029f08bb4e62c505bdae63.gif

点在看

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/513898.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux c url下载文件,OpenCV教程之使用cmake生成MakeFile时下载文件

在编译OpenCV以及其附加模块时,有时会需要一些第三方的库,如果本地没有,会自动下载,下载地址一般为GitHub,结果当然就是卡死在那里,根本无法下载,下面教大家如何解决这种问题。问题重现比如我在…

OpenTelemetry 简析

简介: OpenTelemetry 是 CNCF 的一个可观测性项目,旨在提供可观测性领域的标准化方案,解决观测数据的数据模型、采集、处理、导出等的标准化问题,提供与三方 vendor 无关的服务。 2021.02.10,OpenTelemetry 的 tracing…

涨姿势 | 一文读懂备受大厂青睐的ClickHouse高性能列存核心原理

简介: 本文尝试解读ClickHouse存储层的设计与实现,剖析它的性能奥妙 作者:和君 引言 ClickHouse是近年来备受关注的开源列式数据库,主要用于数据分析(OLAP)领域。目前国内各个大厂纷纷跟进大规模使用&…

xp正版验证补丁_实操web漏洞验证——IIS HTTP.sys 整数溢出漏洞

一、漏洞描述Http.sys 是一个位于 Windows 操作系统核心组件,能够让任何应用程序通过它提供的接口,以 Http 协议进行信息通讯。微软在 Windows 2003 Server 里引进了新的 HTTP API 和内核模式驱动 Http.sys,目的是使基于 Http 服务的程序更有…

普通公司编程水平与BAT大厂相比,差距到底有多大?

前言BAT的牛人多,普通人也多,虽然他们不是每个人都能达到令人仰望的技术水平,但毕竟平台高,所以眼光会变得宽阔;代码要求更为严格,所以普通的程序员也会被逼变得更优秀;身边的牛人多&#xff0c…

mindi linux 使用教程,Linux使用入门教程之tuned

tuned是RHEL6推出的调优工具,可以简单、动态的调整系统。默认的tuned软件包包含9个调优配置文件,对laptop、desktop、server系统的省电策略、磁盘调度算法、缓存、内存脏页回写等等的调节。tuned包安装后有两个服务,tuned和ktune,…

货运物流移动端解决方案:为货运物流行业打造高性能、高粘性的“双端”触点

简介: 在业务碎片化的情景下,怎么通过平台做整合,建立你的专业化运维池? 从 2020 年发展网络货运以来,在互联网和大数据的协作下,传统的物流企业逐渐转向信息化模式,在政策的不断推动下&#xf…

互联网泛娱乐直播安全的解决之道

简介: 阿里云的CDN和视频云产品在直播安全场景下有广泛的应用,阿里巴巴集团的淘宝、优酷等业务的直播正是基于这些产品底层能力,构建了强大的直播安全体系。这个基于阿里巴巴集团自身业务实践打磨出来的架构设计,也被众多上云客户…

wps office2019PC版和Mac版_学用系列WPS教育版的“十八般武艺”

相信不少朋友都在使用wps进行办公学习,但相信不少教师学生肯定不知道其实WPS还很用心提供教育版,里面有不少实用且好用的学习工具可以让我们的学习如虎添翼。今天胖胖老师就为大家介绍一下新版本WPS教育版的“十八般武艺”,欢迎大家一起来尝鲜…

只因“薪水过高”!被欠薪三个月后遭解雇,程序员删库反击

译者 | 弯月 责编 | 王晓曼出品 | 程序人生 (ID:coder _life)非技术出身的公司创始人往往并不了解开发人员的价值。如果他们的产品是科技产品,那么开发人员就是公司的燃料。我从一位同事那里听到了这个故事。故事的主人公叫做小…

linux模拟内存数据落盘,Linux:保证数据安全落盘

背景在很多IO场景中,我们经常需要确保数据已经安全的写到磁盘上,以便在系统宕机重启之后还能读到这些数据。但是我们都知道,linux系统的IO路径还是很复杂的,分为很多层,每一层都可能会有buffer来加速IO读写。同时&…

阿里云高级技术专家周哲:阿里云边缘云原生应用实践

简介: 为什么需要边缘云原生?边缘云原生的技术特性和优势是什么?它可以为行业提供哪些关键能力?又有哪些场景适合边缘云原生呢?在MEC全球应用开发者大会的“MEC开放论坛”上,阿里云高级技术专家周哲进行了《…

dataframe 如何选中某列的一行_PySpark和SparkSQL基础:如何利用Python编程执行Spark(附代码)

作者:Pinar Ersoy翻译:孙韬淳校对:陈振东本文约2500字,建议阅读10分钟本文通过介绍Apache Spark在Python中的应用来讲解如何利用PySpark包执行常用函数来进行数据处理工作。Apache Spark是一个对开发者提供完备的库和API的集群计算…

重磅官宣:Nacos2.0 发布,性能提升 10 倍

简介: 继 Nacos 1.0 发布以来,Nacos 迅速被成千上万家企业采用,并构建起强大的生态。但是随着用户深入使用,逐渐暴露一些性能问题,因此我们启动了 Nacos 2.0 的隔代产品设计,时隔半年我们终于将其全部实现&…

deepin linux 2014 硬盘安装教程,Linux Deepin的硬盘安装

于是装了,问题来了,即使执行了sudo umount -l /cdrom,也不能直接把原来的个人文件分区挂成/home,因为liveCD的内容还放在这个上面(想不通,我只是挂载,又不格式化,怎么就行了?新氧好像…

一站式云原生智能告警运维平台——SLS新版告警发布!

简介: 本文介绍什么是云原生可观测性需求以及告警限制,介绍一站式云原生智能告警运维平台——SLS新版告警。 前言 本篇是SLS新版告警系列宣传与培训的第一篇,后续我们会推出20系列直播与实战培训视频,敬请关注。 系列目录&#…

ansible 修改文件变量_基于ansible的批量配置生成

背景网络运维,我们有很多时间是在准备配置的路上,咱们之前也讲过,从脑海中或者是从自己的宝藏笔记中找出模板,一顿操作猛如虎,Ctrl C Ctrl V。这个过程是十分危险的,因为人不是机器,肯定会出错&…

Fluid — 云原生环境下的高效“数据物流系统”

简介: 为了解决大数据、AI 等数据密集型应用在云原生计算存储分离场景下,存在的数据访问延时高、联合分析难、多维管理杂等痛点问题,南京大学 PASALab、阿里巴巴、Alluxio 在 2020 年 9 月份联合发起了开源项目 Fluid。Fluid 本质上是一个云原…

普通大学生的 Java 开发能力到什么水平才能进大厂?

对于刚开始接触编程的同学来说,选择一门合适的编程语言非常重要。Java一直以来都是被广泛使用的语言,其服务端开发占比90%,83次在TIOBE排行第一,是很多程序员的首选语言。在发展前景方面,Java的就业范围很广&#xff0…

delphi7 如何判定dbgrid两行重复_教你如何在服装上加入好看的毛线刺绣花边

有时候一件旧衣服我们给它添加一些新的元素,会产生意外惊喜的效果。今天,我们就来学学如何用毛线在衣服上刺绣花边,让衣服变得更加漂亮的方法吧!在本教程中,我们使用了一些零头羊毛线,11号木针和用于刺绣的…