01 | 带你快速攻略Redis源码的整体架构
蒋德钧
你好,我是蒋德钧。从今天这节课开始,我们将开启“Redis 代码之旅”,一起来掌握 Redis 的核心设计思想。
不过,在正式开始我们的旅程之前,还需要先做个“攻略”,也就是要了解和掌握 Redis 代码的整体架构。
这是因为,一旦掌握了 Redis 代码的整体架构,就相当于给 Redis 代码画了张全景图。有了这张图,我们再去学习 Redis 不同功能模块的设计与实现时,就可以从图上快速查找和定位这些功能模块对应的代码文件。而且,有了代码的全景图之后,我们还可以对 Redis 各方面的功能特性有个全面了解,这样也便于更加全面地掌握 Redis 的功能,而不会遗漏某一特性。
那么,我们究竟该如何学习 Redis 的代码架构呢?我的建议是要掌握以下两方面内容:
代码的目录结构和作用划分,目的是理解 Redis 代码的整体架构,以及所包含的代码功能类别;
系统功能模块与对应代码文件,目的是了解 Redis 实例提供的各项功能及其相应的实现文件,以便后续深入学习。
实际上,当你掌握了以上两方面的内容之后,即使你要去了解和学习其他软件系统的代码架构,你都可以按照“先面后点”的方法来推进。也就是说,先了解目录结构与作用类别,再对应功能模块与实现文件,这样可以帮助你快速地掌握一个软件系统的代码全景。
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
- 深入了解
- 翻译
- 解释
- 总结
这篇技术文章深入介绍了Redis源码的整体架构,为读者提供了快速了解Redis源码的方法。作者首先分享了学习大型系统软件代码的小窍门,即从了解系统源码的整体目录结构开始,以便快速对代码有个初步认知。接着详细介绍了Redis源码的目录结构,包括deps目录、src目录、tests目录和utils目录,以及两个重要的配置文件。在介绍每个目录的内容时,作者解释了各个子目录的作用和包含的代码内容,以及这些代码对Redis整体架构的重要性。通过本文,读者可以快速了解Redis源码的整体架构,为后续深入学习Redis的相关设计思想奠定了基础。文章还提供了四条代码路径,按照服务器实例、数据库操作、可靠性和可扩展性保证、辅助功能四个维度,梳理了Redis功能源码,方便读者有逻辑、有章法地学习掌握Redis源码,不至于遗漏重要代码。整篇文章内容丰富,为读者提供了系统的学习方法和思路,对于想要深入了解Redis源码的读者来说,是一篇非常有价值的文章。
仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Redis 源码剖析与实战》,新⼈⾸单¥59
《Redis 源码剖析与实战》,新⼈⾸单¥59
立即购买
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
登录 后留言
全部留言(22)
- 最新
- 精选
- lison老师,有幸听了您的集训班,想咨询下 后续章节是否有具体版本和编译工具的介绍,后续好和您保持同步
作者回复: 咱们这次专栏阅读的源码版本是Redis 5.0.8,源码文件可以在 https://github.com/redis/redis/tree/5.0/src 上下载 编译可以就用gcc。 如果同步有问题,可以留言哈
2021-07-261 - Leven请问老师,没有c语言基础适合吗
作者回复: 建议要有些C语言的语法基础,例如基本数据类型、结构体、联合体、宏定义、控制分支、函数调用、指针等这些概念要有。 否则读起来会有些辛苦的
2021-07-26 - Kaito重新看了一下源码目录,结合这篇文章的内容,整理了一下代码分类(忽略.h头文件),这也更清晰一些: 数据类型: - String(t_string.c、sds.c、bitops.c) - List(t_list.c、ziplist.c) - Hash(t_hash.c、ziplist.c、dict.c) - Set(t_set.c、intset.c) - Sorted Set(t_zset.c、ziplist.c、dict.c) - HyperLogLog(hyperloglog.c) - Geo(geo.c、geohash.c、geohash_helper.c) - Stream(t_stream.c、rax.c、listpack.c) 全局: - Server(server.c、anet.c) - Object(object.c) - 键值对(db.c) - 事件驱动(ae.c、ae_epoll.c、ae_kqueue.c、ae_evport.c、ae_select.c、networking.c) - 内存回收(expire.c、lazyfree.c) - 数据替换(evict.c) - 后台线程(bio.c) - 事务(multi.c) - PubSub(pubsub.c) - 内存分配(zmalloc.c) - 双向链表(adlist.c) 高可用&集群: - 持久化:RDB(rdb.c、redis-check-rdb.c)、AOF(aof.c、redis-check-aof.c) - 主从复制(replication.c) - 哨兵(sentinel.c) - 集群(cluster.c) 辅助功能: - 延迟统计(latency.c) - 慢日志(slowlog.c) - 通知(notify.c) - 基准性能(redis-benchmark.c) 下面解答课后问题: Redis 从 4.0 版本开始,能够支持后台异步执行任务,比如异步删除数据,你能在 Redis 功能源码中,找到实现后台任务的代码文件么? 后台任务的代码在 bio.c 中。 Redis Server 在启动时,会在 server.c 中调用 bioInit 函数,这个函数会创建 3 类后台任务(类型定义在 bio.h 中): #define BIO_CLOSE_FILE 0 // 后台线程关闭 fd #define BIO_AOF_FSYNC 1 // AOF 配置为 everysec,后台线程刷盘 #define BIO_LAZY_FREE 2 // 后台线程释放 key 内存 这 3 类后台任务,已经注册好了执行固定的函数(消费者): - BIO_CLOSE_FILE 对应执行 close(fd) - BIO_AOF_FSYNC 对应执行 fsync(fd) - BIO_LAZY_FREE 根据参数不同,对应 3 个函数(freeObject/freeDatabase/freeSlowsMap) 之后,主线程凡是需要把一个任务交给后台线程处理时,就会调用 bio.c 的 bioCreateBackgroundJob 函数(相当于发布异步任务的函数),并指定该任务是上面 3 个的哪一类,把任务挂到对应类型的「链表」下(bio_jobs[type]),任务即发布成功(生产者任务完成)。 消费者从链表中拿到生产者发过来的「任务类型 + 参数」,执行上面任务对应的方法即可。当然,由于是「多线程」读写链表数据,这个过程是需要「加锁」操作的。 如果要找「异步删除数据」的逻辑,可以从 server.c 的 unlink 命令为起点,一路跟代码进去,就会看到调用了 lazyfree.c 的 dbAsyncDelete 函数,这个函数最终会调到上面提到的发布异步任务函数 bioCreateBackgroundJob,整个链条就串起来了。2021-07-2811156
- 悟空聊架构回答每课一问: Redis 从 4.0 版本开始,能够支持后台异步执行任务,比如异步删除数据,你能在 Redis 功能源码中,找到实现后台任务的代码文件么? 我翻看了 3.0 的源码,发现 3.0 就支持后台任务了。在文件 src\bio.c 里面有一个后台任务的函数: bioProcessBackgroundJobs,支持两种后台任务:关闭文件和 AOF 文件的 fsync 也是放到后台执行的。 (fsync 就是执行命令后将命令写到日志中,提供了三种策略:Always,同步写回,Everysec,每秒写回,No,操作系统控制的写回。) 疑问:根据 3.0 源码,Redis 3.0 其实就已经有后台任务了,老师在文中说的 4.0 才开始支持后台任务,我没理解。 然后我又去翻了下 4.0 的源码,增加一种后台任务:BIO_LAZY_FREE。 当任务类型等于 BIO_LAZY_FREE 时,针对不同的传参,可以释放对象、数据库、跳跃表。 对于释放可以稍微说一下,释放的源码在这个文件里面:\src\lazyfree.c,相对 3.0 来说,这个文件是新增加的。 关于对象的释放,我们可以联想到 Java 的垃圾回收算法:可达性分析算法,但是 Redis 的垃圾回收算法用的是引用计数算法,另外 PHP 的垃圾回收算法用的也是引用计数(扩展下:用了多色标记的方式,来识别垃圾,详细参考这里:https://mp.weixin.qq.com/s/n6PGIgfZ8vXUZ1rkU5Otog),所以别再说引用计数不能用做垃圾回收了哦。 而对于 Redis 释放对象来说,会减少引用的次数,调用的是这个函数:decrRefCount(o); 根据函数的名字也容易理解。 吐槽下:Github 上下载源码总是下载失败,为了其他同学们方便下载,我整理了多套源码的下载地址,都是国内的网盘链接,只有几MB 大小,下载比较快的。 http://www.passjava.cn/#/12.Redis/00.DownloadRedis2021-07-27614
- Ethan New评论区也太强了吧,瑟瑟发抖2021-07-2810
- 陌关于作业,如果一开始对 Redis 源码不熟悉的话,我们可以借用 GDB 工具来回答 Redis 有哪些后台任务: 1) 添加 -g 的编码参数,向编译文件中添加调试信息,以便使用 GDB: make CFLAGS="-g -O0" 2) cd src && gdb redis-server 3) 在 aeMain 函数处打一个断点,然后再使得程序运行至此处: break aeMain run 4) 查看线程信息: info threads 这时候我们就能够看到 4 个线程的相关信息,分别是 redis-server、bio_close_file、bio_aof_fsync、bio_lazy_free,然后就可以按线程名称再去源码中查找了。2021-07-2918
- 可怜大灰狼我的第一反应应该是从unlink命令入手查找。首先肯定是server.c中redisCommandTable[]中的unlinkCommand,找到了lazyfree.c中dbAsyncDelete方法,然后找到了bio.c中bioCreateBackgroundJob方法,很显然bio.h中加了一种后台IO任务类型:BIO_LAZY_FREE=2。我记得我看3.0代码还只有BIO_CLOSE_FILE和BIO_AOF_FSYNC2021-07-265
- Darrenbio.c 在5.x的源码中,后台异步执行又3个子线程 #define BIO_NUM_OPS 3 #define BIO_CLOSE_FILE 0 /* Deferred close(2) syscall. */ #define BIO_AOF_FSYNC 1 /* Deferred AOF fsync. */ #define BIO_LAZY_FREE 2 /* Deferred objects freeing. */ bioInit方法中通过pthread_create创建BIO_NUM_OPS子线程,不同线程的任务在static list *bio_jobs[BIO_NUM_OPS]中存储。2021-07-265
- 小五1 Redis 支持 3 大类型的后台任务,它们定义在 bio.h 文件中: /* Background job opcodes 后台作业操作码 * 1 处理关闭文件 * 2 AOF 异步刷盘 * 3 lazyfree */ #define BIO_CLOSE_FILE 0 /* Deferred close(2) syscall. */ #define BIO_AOF_FSYNC 1 /* Deferred AOF fsync. */ #define BIO_LAZY_FREE 2 /* Deferred objects freeing. */ 在 Redis 服务器启动时,会创建以上三类后台线程,然后阻塞等待任务的到来。处理关闭文件和 AOF 异步刷盘异步任务比较好理解,lazyfree 类型的异步任务场景就比较多了,比如下面几种情况: 1)删除数据:配置 lazyfree_lazy_user_del ,使用 unlink , 都可能将删除封装成任务放到 bio_jobs 任务队列中 2) 定期删除时,如果配置 lazyfree_lazy_expire ,那么可能将删除封装成任务放到 bio_jobs 任务队列中 2 后台创建的以上三种 bio 后台线程会不断轮询 bio_jobs 任务队列中的任务,并分门别类的处理对应的任务。逻辑操作定义在 bio.c 文件中 3 在 Redis 6.0 中增加了 io 多线程,在 networking.c 中定义了 io 线程的任务队列,以及创建 io_threads_num 个 io 线程,这些 io 线程会不断轮询 IO 读写任务。2021-07-273
- Kang请问下老师,各个源码目录的作用是从那里获取到的,mysql的话也会有相应解释吗2021-07-2613
收起评论