当前位置: 首页 > news >正文

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 的设计与实现时,就会非常方便,不用在不同的文件之间来回跳转了
  • 辅助功能

  • 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\LaTeXLATE​X在线编译器,它可以让你与合作者共同在线编辑文档。但是默认的免费账号仅能邀请一个合作者。那么如何增加合作者的数量呢? Overleaf推出了一个奖励计划,你邀请其他人注册Overleaf&#xf…...

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…...

网络原理之初识

目录 一. 网络互连 1. 局域网 2. 广域网 二. 网络通信基础 1. IP 地址 2. 端口号 3. 网络协议 4. 协议分层 5. TCP/IP 五层网络模型 (简述) 6. 网络数据传输的基本流程 一. 网络互连 随着时代的发展,越来越需要计算机之间互相通信&am…...

CAN总线开发一本全(4) - FlexCAN的驱动程序

CAN总线开发一本全(4) - FlexCAN的驱动程序 苏勇,2023年2月 文章目录CAN总线开发一本全(4) - FlexCAN的驱动程序引言从MindSDK获取FlexCAN驱动程序数据结构配置通信引擎的结构体类型访问MB的结构体类型配置ID过滤器的…...

如何分析linux tcp/ip 丢包问题

引用手把手教你用Dropwatch诊断问题通过dropwatch定位系统内核丢包Finding out if/why a server is dropping packetsgithub source coed: pavel-odintsov/drop_watchHow to drop a packet in Linux in more ways than one试试Linux下的ip命令,ifconfig已经过时了Ho…...

旅游规划(树型dp)

W 市的交通规划出现了重大问题,市政府下定决心在全市各大交通路口安排疏导员来疏导密集的车流。 但由于人员不足,W 市市长决定只在最需要安排人员的路口安排人员。 具体来说,W 市的交通网络十分简单,由 n 个交叉路口和 n−1 条街道…...

【C++】string类的模拟实现

当你将放弃作为一种习惯,你一辈子也不会有出息… 文章目录一、Default member functions1.Constructor2.Copy constructor(代码重构:传统写法和现代写法)3.operator(代码重构:传统写法和现代写法&#xff…...

笔记(一)——STL容器

容器分类:序列式容器:每个元素都有固定位置,取决于插入的时机和地点,和元素无关,如vector、deque、list、stack、queue。关联式容器:元素位置取决于特定的排序准则,和插入顺序无关,如…...

红黑树

红黑树是一个相对的平衡,减少了旋转的消耗 一个节点不是红的就是黑的根节点是黑的一个节点是红的,孩子是黑的(没有连续的红色节点)对于每个节点,从该节点到后代节点的简单路径,都包含相同的黑色&#xff0…...

RIP路由协议的更新(电子科技大学TCP/IP第二次实验)

一.实验目的 1、掌握 RIP 协议在路由更新时的发送信息和发送方式 2、掌握 RIP 协议的路由更新算法 二.预备知识 1、静态路由选择和动态路由选择 2、内部网关协议和外部网关协议 3、距离向量路由选择 三.实验原理 RIP 协议&#xff08…...

基于JWT实现用户身份认证

常见场景 账号/密码登录、手机号验证码登录、微信扫码登录 解决方案 基于Session认证方案 什么是session认证方案 服务端生成httpsession认证(内存-sessionId)sessionId写到浏览器cookie浏览器请求的header中自动带sessionId到服务端服务端校验sessionId是否合法 优点 .…...

SaltStack 远程命令执行漏洞(CVE-2020-16846)

目录 (一)漏洞描述 (二)漏洞复现 1、在vulhub上启动docker 2、访问docker靶机 https /ip:8000...