Redis源码入门零基础学起,慢慢摸索理解那些底层细节
- 问答
- 2026-01-12 22:13:38
- 3
对于一个零基础想读Redis源码的人来说,最大的障碍可能就是觉得它庞大又复杂,不知道从哪里开始,别担心,这很正常,关键不是要一口气吃成胖子,而是找到一条小路,慢慢走进去,这条小路,通常就是从Redis最核心、最简单的东西开始。
第一步:别急着看网络处理,先看懂它怎么存数据
很多教程会建议从main函数开始,顺着网络读写的流程看,但对于新手,这很容易让人迷失在复杂的事件循环、套接字细节里,更直接的方法是,先忽略数据是怎么来的,聚焦于数据是怎么存的,Redis的本质是一个内存数据库,它的核心就是如何高效地组织和管理那些键值对。
第一个目标应该是理解Redis的“键值对”到底在内存中长什么样,这就引出了Redis最基本的数据结构:字典(在源码里叫dict),你可以把字典想象成一个超级升级版的哈希表,在src目录下,找到dict.h和dict.c这两个文件,它们就是字典的实现。
怎么读呢?
-
看
dict.h:这里面定义了构成字典的几个关键“积木”。dictEntry:这是最小的单位,代表一个键值对,它本身是一个链表结构,这是为了解决哈希冲突(就是两个不同的键算出来的位置一样,用链表把它们串起来)。dictht:代表整个哈希表的结构,里面包含了哈希表数组(每个数组元素其实是一个指向dictEntry链表的指针)、表的大小、已经用了多少等信息。dict:这是最重要的结构,它代表一个完整的字典,它里面包含了两个dictht(为什么是两个?为了渐进式扩容,这个后面会很有趣),以及一些函数指针(比如用来计算哈希值的函数,比较键的函数),这些函数指针让字典变得通用,可以存储各种类型的数据。
-
看
dict.c:看完头文件,知道了有哪些积木,现在就看怎么用这些积木盖房子,重点关注几个最基础的操作函数:dictAdd:如何添加一个键值对,跟着它的代码走,看看它是怎么计算哈希值的,怎么根据哈希值找到数组位置的,如果那个位置已经有元素了(哈希冲突),它是怎么把新元素挂到链表上的。dictFind:如何查找一个键,这个过程和添加很像,也是计算哈希值,找到位置,然后顺着链表一个个比较键是否相等。dictDelete:如何删除一个键值对,这涉及到从链表中删除一个节点,想想数据结构课上学过的链表删除操作。
当你把字典的增删改查大概看明白后,你会有一个恍然大悟的感觉:哦,原来Redis最底层存东西就是这么回事啊!这就打下了第一个坚实的基础。
第二步:从字符串键入手,串起整个流程

现在你知道了数据存哪儿了,那一个具体的命令,比如我们最常用的SET key value,是怎么和这个字典联系起来的呢?
-
找命令表:在src目录下搜索
redisCommandTable(通常在server.c文件里),这是一个巨大的数组,里面定义了Redis所有命令的信息。SET命令对应哪个处理函数?它的参数个数要求是什么?在这里你能找到SET命令对应的处理函数可能是setCommand。 -
跟踪
setCommand函数:这个函数可能在t_string.c文件中,这个函数做的事情很直观:- 检查参数合法性(比如参数个数对不对)。
- 核心就是调用
setGenericCommand之类的函数。 - 继续跟进去,你会发现它最终会调用
dbAdd(如果key不存在)或dbOverwrite(如果key存在)。
-
连接数据库和字典:
dbAdd和dbOverwrite这些函数在db.c文件中,你会发现,Redis服务器有一个全局变量server(server.h中定义),里面有一个数组db,代表多个数据库,每个数据库(redisDb结构)里,最重要的就是一个字典,叫做dict,它就是用来存储所有键值对的!到这里,整个链条就通了:SET命令->setCommand函数->dbAdd/dbOverwrite-> 操作某个数据库里的dict字典。
通过跟踪一个最简单的SET命令,你就能看到用户请求是如何一步步转化为对底层字典的操作的,这个过程会让你对Redis的架构有更感性的认识。

第三步:探索其他数据结构
字符串是简单的,因为它的值直接就是一个字符串,但Redis还有列表、集合、哈希对象等,这些复杂的数据结构,在Redis中是如何实现的呢?这就引出了Redis一个非常精妙的设计:对象系统(在object.c和server.h中定义的redisObject结构)。
每个键值对,不仅仅是一个dictEntry那么简单,它的“值”部分实际上是一个redisObject结构,这个结构体里有个关键的字段叫type(标识这是字符串、列表还是集合等),还有一个encoding字段(标识这个类型的底层是用什么数据结构实现的,比如列表可以用链表实现,也可以用压缩列表实现),最后是一个ptr指针,指向真正存储数据的内存地址。
举个例子,当你创建一个列表键时:
type是OBJ_LIST。- 如果列表元素少且小,
encoding可能是OBJ_ENCODING_QUICKLIST(快速列表),ptr就指向一个quicklist的结构。 - 你可以接着去读
quicklist.h和quicklist.c,看看它又是如何结合压缩列表和双向链表来优化内存和性能的。
用这种方式,你可以一种数据结构一种数据结构地去攻克,每学一种,都是对底层细节的一次探索。
一些实用的摸索建议:
- 画图:一定要动手画!画字典的结构,画链表怎么连接,画对象之间的关系,图形比纯文字记忆深刻得多。
- 写注释:把源码clone到本地,用IDE打开,在你看懂的函数旁边,用中文写下你自己的注释,总结这个函数做了什么,这是最有效的学习方式。
- gdb调试:如果条件允许,学习简单的gdb命令,启动一个Redis-server,然后打断点(比如在
setCommand函数入口打一个断点),一步一步执行,观察变量的变化,这是理解程序运行时的“上帝视角”。 - 从小处着手:不要试图一开始就理解持久化(RDB/AOF)、主从复制、集群这些超级复杂的模块,牢牢抓住“单线程处理命令”和“数据存储”这个核心,根基稳了,再去看那些高级功能。
读源码是一个反复的过程,今天看不懂,放一放,明天再看可能就懂了,最重要的是你开始了第一步,并且带着好奇心去摸索那些看似黑盒的细节。
本文由歧云亭于2026-01-12发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:http://waw.haoid.cn/wenda/79561.html
