Redis源码---整体架构
目录
前言
Redis目录结构
前言
deps目录
src 目录
tests 目录
utils 目录
重要的配置文件
Redis 功能模块与源码对应
前言
服务器实例
数据库数据类型与操作
高可靠性和高可扩展性
辅助功能
-
前言
- 以先面后点的方法推进
- 无特殊说明,都是基于 Redis 5.0.8 版本的
- 掌握了 Redis 代码的整体架构,就相当于给 Redis 代码画了张全景图
- 有了这张图,再去学习 Redis 不同功能模块的设计与实现时,就可以从图上快速查找和定位这些功能模块对应的代码文件
- 而且,有了代码的全景图之后,还可以对 Redis 各方面的功能特性有个全面了解,这样也便于更加全面地掌握 Redis 的功能,而不会遗漏某一特性
- 学习 Redis 的代码架构要掌握以下两方面内容:
- 1--代码的目录结构和作用划分
- 目的是理解 Redis 代码的整体架构,以及所包含的代码功能类别;
- 2--系统功能模块与对应代码文件,目的是了解 Redis 实例提供的各项功能及其相应的实现文件,以便后续深入学习
-
Redis目录结构
-
前言
- 对于 Redis 来说,在它的源码总目录下
- 一共包含了deps、src、tests、utils四个子目录
- 这四个子目录分别对应了 Redis 中发挥不同作用的代码
-
deps目录
- 这个目录主要包含了 Redis 依赖的第三方代码库
- 包括
- Redis 的 C 语言版本客户端代码hiredis
- jemalloc 内存分配器代码
- readline 功能的替代代码 linenoise
- lua 脚本代码
- 这部分代码的一个显著特点,就是它们可以独立于 Redis src 目录下的功能源码进行编译
- 也就是说,它们可以独立于 Redis 存在和发展
- 下面这张图显示了 deps 目录下的子目录内容:
- 那么,为什么在 Redis 源码结构中会有第三方代码库目录呢?
- 其实主要有两方面的原因:
- 一方面
- Redis 作为一个用 C 语言写的用户态程序,它的不少功能是依赖于标准的 glibc 库提供的
- 比如内存分配、行读写(readline)、文件读写、子进程/线程创建等
- 但是,glibc库提供的某些功能实现,效率并不高
- 例子:glibc 库中实现的内存分配器的性能就不是很高,它的内存碎片化情况也比较严重
- 因此为了避免对系统性能产生影响,Redis 使用了 jemalloc 库替换了 glibc 库的内存分配器
- 可是,jemalloc 库本身又不属于 Redis 系统自身的功能,把它和 Redis 功能源码放在一个目录下并不合适
- 所以,Redis 使用了专门的 deps 目录来保存这部分代码
- 另一方面
- 有些功能是 Redis 运行所需要的
- 但是这部分功能又会独立于 Redis 进行开发和演进
- 这种类型最为典型的功能代码,就是 Redis 的客户端代码
- Redis 作为 Client-Server 架构的系统,访问 Redis 离不开客户端的支撑
- 此外,Redis 自身功能中的命令行 redis-cli、基准测试程序 redis-benchmark 以及哨兵,都需要用到客户端来访问 Redis 实例
- 不过针对客户端的开发,只要保证客户端和实例交互的过程满足 RESP 协议就行
- 客户端和实例的功能可以各自迭代演进
- 所以在 Redis 源码结构中,C 语言版本的客户端hiredis,就被放到了 deps 目录中,以便开发人员自行开发和改进客户端功能
- 总而言之,对于 deps 目录来说
- 只需要记住它主要存放了三类代码:
- 一是 Redis 依赖的、实现更加高效的功能库,如内存分配
- 二是独立于 Redis 开发演进的代码,如客户端
- 三是 lua 脚本代码
-
src 目录
- 这个目录里面包含了 Redis 所有功能模块的代码文件,也是 Redis 源码的重要组成部分
- 同样,先来看下 src 目录下的子目录结构:
- 可以发现,src 目录下只有一个 modules 子目录,其中包含了一个实现 Redis module 的示例代码
- 剩余的源码文件都是在 src 目录下,没有再分下一级子目录
- 因为 Redis 的功能模块实现是典型的 C 语言风格,不同功能模块之间不再设置目录分隔
- 而是通过头文件包含来相互调用
- 这样的代码风格在基于 C 语言开发的系统软件中,也比较常见,比如 Memcached 的源码文件也是在同一级目录下
- 所以,当使用 C 语言来开发软件系统时,就可以参考 Redis 的功能源码结构,用一个扁平的目录组织所有的源码文件,这样模块相互间的引用也会很方便
-
tests 目录
- 在软件产品的开发过程中,除了第三方依赖库和功能模块源码以外,通常还需要在系统源码中,添加用于功能模块测试和单元测试的代码
- 而在 Redis 的代码目录中,就将这部分代码用一个 tests 目录统一管理了起来
- Redis 实现的测试代码可以分成四部分,分别是
- 单元测试(对应 unit 子目录)
- Redis Cluster 功能测试(对应 cluster 子目录)
- 哨兵功能测试(对应 sentinel 子目录)
- 主从复制功能测试(对应 integration 子目录)
- 这些子目录中的测试代码使用了 Tcl 语言(通用的脚本语言)进行编写,主要目的就是方便进行测试
- 另外,每一部分的测试都是一个测试集合,覆盖了相应功能模块中的多项子功能测试
- 比如:
- 在单元测试的目录中,可以看到有针对过期 key 的测试(expire.tcl)、惰性删除的测试(lazyfree.tcl),以及不同数据类型操作的测试(type 子目录)等
- 而在 Redis Cluster 功能测试的目录中,可以看到有针对故障切换的测试(failover.tcl)、副本迁移的测试(replica-migration.tcl)等
- 不过在 tests 目录中,除了有针对特定功能模块的测试代码外,还有一些代码是用来支撑测试功能的
- 这些代码在 assets、helpers、modules、support 四个目录中
- 这里有张图,展示了 tests 目录下的代码结构和层次:
-
utils 目录
- 在 Redis 开发过程中,还有一些功能属于辅助性功能,包括用于创建 Redis Cluster 的脚本、用于测试 LRU 算法效果的程序,以及可视化 rehash 过程的程序
- 在 Redis 代码结构中,这些功能代码都被归类到了 utils 目录中统一管理
- 下图展示了 utils 目录下的主要子目录:
-
重要的配置文件
- 除了 deps、src、tests、utils 四个子目录以外,Redis 源码总目录下其实还包含了两个重要的配置文件,一个是 Redis 实例的配置文件 redis.conf,另一个是哨兵的配置文件sentinel.conf
- 当需要查找或修改 Redis 实例或哨兵的配置时,就可以直接定位到源码总目录下
-
Redis 功能模块与源码对应
-
前言
- Redis 代码结构中的 src 目录,包含了实现功能模块的 123 个代码文件
- 在这 123 个代码文件中,对于某个功能来说,一般包括了实现该功能的 C 语言文件(.c 文件)和对应的头文件(.h 文件)
- 比如,dict.c 和 dict.h 就是用于实现哈希表的 C 文件和头文件
- 那么,该如何将这 123 个文件和 Redis 的主要功能对应上呢?
- 其实,Redis 代码文件的命名非常规范,文件名中就体现了该文件实现的主要功能
- 比如,对于 rdb.h 和 rdb.c 这两个代码文件来说,从文件名上,就可以看出来它们是实现内存快照RDB 的对应代码
- 所以这里,为了能快速定位源码,就分别按照 Redis 的服务器实例、数据库操作、可靠性和可扩展性保证、辅助功能四个维度,把 Redis 功能源码梳理成了四条代码路径
-
服务器实例
- Redis 在运行时是一个网络服务器实例,因此相应地就需要有代码实现服务器实例的初始化和主体控制流程
- 而这是由 server.h/server.c 实现的,Redis 整个代码的 main入口函数也是在 server.c 中
- 如果想了解 Redis 是如何开始运行的,那么就可以从server.c 的 main 函数开始看起
- 当然,对于一个网络服务器来说,它还需要提供网络通信功能
- Redis 使用了基于事件驱动机制的网络通信框架,涉及的代码文件包括 ae.h/ae.c,ae_epoll.c,ae_evport.c,ae_kqueue.c,ae_select.c
- 关于事件驱动框架的具体设计思路与实现方法,后续会详细介绍
- 而除了事件驱动网络框架以外,与网络通信相关的功能还包括底层 TCP 网络通信和客户端实现
- Redis 对 TCP 网络通信的 Socket 连接、设置等操作进行了封装,这些封装后的函数实现在anet.h/anet.c 中
- 这些函数在 Redis Cluster 创建和主从复制的过程中,会被调用并用于建立 TCP 连接
- 除此之外,客户端在 Redis 的运行过程中也会被广泛使用,比如实例返回读取的数据、主从复制时在主从库间传输数据、Redis Cluster 的切片实例通信等,都会用到客户端
- Redis 将客户端的创建、消息回复等功能,实现在了 networking.c 文件中,如果想了解客户端的设计与实现,可以重点看下这个代码文件
- 这里也总结了与服务器实例相关的功能模块及对应的代码文件:
-
数据库数据类型与操作
- Redis 数据库提供了丰富的键值对类型,其中包括了 String、List、Hash、Set 和 Sorted Set这五种基本键值类型
- 此外,Redis 还支持位图、HyperLogLog、Geo 等扩展数据类型
- 而为了支持这些数据类型,Redis 就使用了多种数据结构来作为这些类型的底层结构
- 比如,String 类型的底层数据结构是 SDS,而 Hash 类型的底层数据结构包括哈希表和压缩列表
- 不过,因为 Redis 实现的底层数据结构非常多,所以这里把这些底层结构和它们对应的键值对类型,以及相应的代码文件列在了下表中,可以用这张表来快速定位代码文件:
- 除了实现了诸多的数据类型以外,Redis 作为数据库,还实现了对键值对的新增、查询、修改和删除等操作接口,这部分功能是在 db.c 文件实现的
- 当然,Redis 作为内存数据库,其保存的数据量受限于内存大小
- 因此,内存的高效使用对于Redis 来说就非常重要
- Redis 是如何优化内存使用的呢?
- 实际上,Redis 是从三个方面来优化内存使用的,分别是内存分配、内存回收,以及数据替换
- 首先,在内存分配方面,Redis 支持使用不同的内存分配器,包括 glibc 库提供的默认分配器tcmalloc、第三方库提供的 jemalloc
- Redis 把对内存分配器的封装实现在了zmalloc.h/zmalloc.c
- 其次,在内存回收上,Redis 支持设置过期 key,并针对过期 key 可以使用不同删除策略,这部分代码实现在 expire.c 文件中
- 同时,为了避免大量 key 删除回收内存,会对系统性能产生影响,Redis 在 lazyfree.c 中实现了异步删除的功能
- 所以这样,就可以使用后台 IO线程来完成删除,以避免对 Redis 主线程的影响
- 最后,针对数据替换,如果内存满了,Redis 还会按照一定规则清除不需要的数据,这也是Redis 可以作为缓存使用的原因
- Redis 实现的数据替换策略有很多种,包括 LRU、LFU 等经典算法
- 这部分的代码实现在了 evict.c 中
- 同样,这里也把和 Redis 数据库数据类型与操作相关的功能模块及代码文件,总结成了一张图:
-
高可靠性和高可扩展性
- 首先,虽然 Redis 一般是作为内存数据库来使用的,但是它也提供了可靠性保证
- 这主要体现在 Redis 可以对数据做持久化保存,并且它还实现了主从复制机制,从而可以提供故障恢复的功能
- 这部分的代码实现比较集中,主要包括以下两个部分:
- 1--数据持久化实现
- Redis 的数据持久化实现有两种方式:内存快照 RDB 和 AOF 日志,分别实现在了rdb.h/rdb.c 和 aof.c 中
- 注意,在使用 RDB 或 AOF 对数据库进行恢复时,RDB 和 AOF 文件可能会因为 Redis 实例所在服务器宕机,而未能完整保存,进而会影响到数据库恢复
- 因此针对这一问题,Redis 还实现了对这两类文件的检查功能,对应的代码文件分别是 redis-check-rdb.c 和 redis-check-aof.c
- 2--主从复制功能实现
- Redis 把主从复制功能实现在了 replication.c 文件中
- 另外还需要知道的是,Redis 的主从集群在进行恢复时,主要是依赖于哨兵机制,而这部分功能则直接实现在了 sentinel.c 文件中
- 其次,与 Redis 实现高可靠性保证的功能类似,Redis 高可扩展性保证的功能,是通过 Redis Cluster 来实现的,这部分代码也非常集中,就是在 cluster.h/cluster.c 代码文件中
- 所以这样在学习 Redis Cluster 的设计与实现时,就会非常方便,不用在不同的文件之间来回跳转了
- 1--数据持久化实现
-
辅助功能
- Redis 还实现了一些用于支持系统运维的辅助功能
- 比如,为了便于运维人员查看分析不同操作的延迟产生来源,Redis 在latency.h/latency.c 中实现了操作延迟监控的功能
- 为了便于运维人员查找运行过慢的操作命令,Redis 在 slowlog.h/slowlog.c 中实现了慢命令的记录功能,等等
- 此外,运维人员有时还需要了解 Redis 的性能表现,为了支持这一目标,Redis 实现了对系统进行性能评测的功能,这部分代码在 redis-benchmark.c 中
相关文章:

Redis源码---整体架构
目录 前言 Redis目录结构 前言 deps目录 src 目录 tests 目录 utils 目录 重要的配置文件 Redis 功能模块与源码对应 前言 服务器实例 数据库数据类型与操作 高可靠性和高可扩展性 辅助功能 前言 以先面后点的方法推进无特殊说明,都是基于 Redis 5.0.…...

基于springboot+vue的校园招聘系统
博主主页:猫头鹰源码 博主简介:Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容:毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…...

SAP MM学习笔记1-SAP中扩张的概念,如何将一个物料从工厂A扩张到工厂B
MM中在创建物料的时候,最低也得创建如下5个view。 基本数据1 基本数据2 购买管理 会计1 会计2 1,扩张是什么 有时候,你想增加其他的View,比如保管场所 等,你不能用MM02来做编辑,要用MM01来做扩张。这就是扩…...

【Python】Numpy数组的切片、索引详解:取数组的特定行列
【Python】Numpy数组的切片、索引详解:取数组的特定行列 文章目录【Python】Numpy数组的切片、索引详解:取数组的特定行列1. 介绍2. 切片索引2.1 切片索引先验知识2.1 一维数组的切片索引2.3 多维数组的切片索引3. 数组索引(副本)…...
2023年全国最新交安安全员精选真题及答案6
百分百题库提供交安安全员考试试题、交安安全员考试预测题、交安安全员考试真题、交安安全员证考试题库等,提供在线做题刷题,在线模拟考试,助你考试轻松过关。 51.安全生产资金保障制度建立后关键在于落实,各施工企业在落实安全生…...

JavaScript 闭包【自留】
闭包的概念理解 闭包的定义 ✅ 这里先来看一下闭包的定义,分成两个:在计算机科学中和在JavaScript中。 ✅ 在计算机科学中对闭包的定义(维基百科): 闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures);是在支持头等函数…...

【MySQL】什么是意向锁 IS IX 及值得学习的思想
文章目录前言行锁和表锁使用意向锁意向锁的算法意向锁的思想JDK 中相似的思想前言 之前看 MySQL 都刻意忽略掉了 IS 和 IX 锁,今天看 《MySQL 是怎样运行的》,把意向锁讲的很通透,本篇博文提炼一下思想。 I: Intention Lock(意向…...
python多线程实现
用于线程实现的Python模块 Python线程有时称为轻量级进程,因为线程比进程占用的内存少得多。 线程允许一次执行多个任务。 在Python中,以下两个模块在一个程序中实现线程 - _thread模块threading模块 这两个模块之间的主要区别在于_thread模块将线程视…...

macOS使用CodeRunner快速配置fortran环境
个人网站:xzajyjs.cn 由于一些项目的缘故,需要有fortran的需求,但由于是M1 mac的缘故,不能像windows那样直接使用vsivf这种经典配置。搜了一下网上主流的跨平台方案,主要是gfortran,最近用Coderunner(主要…...

【云原生】k8s 离线部署讲解和实战操作
文章目录一、概述二、前期准备1)节点信息2)修改主机名和配置hosts3)配置ssh互信4)时间同步5)关闭防火墙6)关闭 swap7)禁用SELinux8)允许 iptables 检查桥接流量三、开始部署1&#x…...
【Kubernetes】第十一篇 - 滚动发布的介绍与实现
一,前言 上一篇,介绍了灰度发布和流量切分的集中方式,以及如何实现 k8s 的灰度发布; 本篇,介绍滚动发布的实现; 二,滚动发布简介 滚动发布 滚动发布,则是我们一般所说的无宕机发…...
【尊享版】如何系统构建你的思维认知模型?
超友们,早上好,国庆节快乐~ 今天为你带来的分享是《如何系统构建你的思维认知模型?》,主要分为三个部分: 第一部分:【实现爆发式成长的 10 个思维模型】 第二部分:【6 个不可不知的…...
urho3D编码约定
缩进样式类似于Allman(BSD),即在控制语句的下一行使用大括号,在同一级别缩进。在switch-case语句中,case与switch语句处于相同的缩进级别。 缩进使用4个空格而不是制表符。不应保留空行上的缩进。 类和结构名称以大写…...

Overleaf推广奖励:增加合作者的数量、解锁Dropbox同步和项目修改历史
Overleaf推广奖励 Overleaf是一个LaTeX\LaTeXLATEX在线编译器,它可以让你与合作者共同在线编辑文档。但是默认的免费账号仅能邀请一个合作者。那么如何增加合作者的数量呢? Overleaf推出了一个奖励计划,你邀请其他人注册Overleaf…...

ChatGPT的互补工具Perplexity的详细使用方法(持续更新)
大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,科大讯飞比赛第三名,CCF比赛第四名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…...

【Linux驱动开发100问】如何编译Linux内核?
🥇今日学习目标:如何编译Linux内核? 🤵♂️ 创作者:JamesBin ⏰预计时间:10分钟 🎉个人主页:嵌入式悦翔园个人主页 🍁专栏介绍:Linux驱动开发100问 如何编译…...

15、条件概率、全概率公式、贝叶斯公式、马尔科夫链
条件概率定义:设A、B是两个事件,且,P(A) > 0 则称 为事件A发生的条件下事件B的条件概率对这个式子进行变形,即可得到概率的乘法公式:P(A) > 0 时,则P(B) > 0 时,则乍一看,…...

Eureka服务注册与发现
注册中心是分布式开发的核心组件之一,而Eureka是spring cloud推荐的注册中心实现。简单分析一下Eureka的原理。Eureka基础概念与流程1、服务注册在微服务架构中,一个服务提供者本质上也是一个Eureka客户端。启动时,会调用Eureka所提供的服务注…...
20230226 引用类型和指针类型的区别 - chatGPT
绝了,把chatGPT当百度之后真爽! 引用类型和指针类型都是C语言中的重要概念,它们都提供了访问和操作内存的方法,但它们之间有几个关键的区别。 1. 定义和初始化方式不同 指针类型的变量定义和初始化的方式是通过使用*符号来声明…...

《操作系统》——第二章 进程与线程
目录 2.1.1进程的概念、组成、特征 2.1.2进程的状态与转换、进程的组织 2.1.3进程控制 2.1.4进程通信 2.1.5线程的概念 2.1.6线程的实现方式和多线程模型 2.2.1调度的概念、层次 2.2.2进程调度的时机、切换与过程、方式 2.2.4调度算法的评价指标 2.2.5调度算法(1) 2…...
RestClient
什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端,它允许HTTP与Elasticsearch 集群通信,而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级ÿ…...

第19节 Node.js Express 框架
Express 是一个为Node.js设计的web开发框架,它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用,和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...

React第五十七节 Router中RouterProvider使用详解及注意事项
前言 在 React Router v6.4 中,RouterProvider 是一个核心组件,用于提供基于数据路由(data routers)的新型路由方案。 它替代了传统的 <BrowserRouter>,支持更强大的数据加载和操作功能(如 loader 和…...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...

YSYX学习记录(八)
C语言,练习0: 先创建一个文件夹,我用的是物理机: 安装build-essential 练习1: 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件,随机修改或删除一部分,之后…...
pam_env.so模块配置解析
在PAM(Pluggable Authentication Modules)配置中, /etc/pam.d/su 文件相关配置含义如下: 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块,负责验证用户身份&am…...
C++中string流知识详解和示例
一、概览与类体系 C 提供三种基于内存字符串的流,定义在 <sstream> 中: std::istringstream:输入流,从已有字符串中读取并解析。std::ostringstream:输出流,向内部缓冲区写入内容,最终取…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...
【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具
第2章 虚拟机性能监控,故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令:jps [options] [hostid] 功能:本地虚拟机进程显示进程ID(与ps相同),可同时显示主类&#x…...

ArcGIS Pro制作水平横向图例+多级标注
今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...