Redis源码速读,带你快速突破极限理解那些隐藏细节
- 问答
- 2026-01-18 22:31:37
- 4
基于对Redis多个版本源码的阅读,特别是dict.c、sds.c、server.c等核心文件,以及相关技术博客和讨论的分析)
想真正理解Redis为什么这么快、这么稳定,光看配置文件是不够的,必须得钻进它的源码里瞧瞧,今天我们就抛开那些复杂的概念,像看故事一样,看看Redis的作者们在代码里埋了哪些精妙的“彩蛋”。
第一幕:简单字符串SDS的“小心机”
Redis没用C语言自带的字符数组,而是自己搞了个叫SDS(Simple Dynamic String)的结构。(来源:sds.h/sds.c)这东西看起来简单,但藏着两个让你性能飞起的细节。
它“作弊”了,一个SDS结构,除了存字符串本身,还在你看不见的地方(字符串开头前面)藏了一些“头信息”,比如这个字符串的长度和剩余空闲空间有多少,你可能会问,这有啥用?太有用了!C语言的strlen函数想知道一个字符串多长,得从头开始一个一个数,直到遇到结束符\0,这叫O(N)复杂度,而SDS直接看一眼自己藏起来的“头信息”里的长度字段,瞬间就知道了,这是O(1)复杂度,Redis里成千上万的键,获取键名长度是个高频操作,这点优化积累起来就不得了。
它很“懂事”,会主动管理内存,当你往一个SDS字符串后面追加数据时,它会先检查下“头信息”里剩下的空闲空间够不够,如果够,就直接用,不够了再扩容,但扩容不是只扩刚刚好,它可能会多扩一点(比如翻倍),这就是“预分配”,这样,你下次再追加时,很可能就不用再申请内存了,这种用空间换时间的策略,是Redis保持高性能的常用手段。

第二幕:字典Dict的“渐进式”搬家
Redis的键值对主要存在一个巨大的字典里,当数据越来越多,这个字典就需要扩容,也就是搬到一个更大的新家里去。(来源:dict.c)
最傻的办法是:停止服务,把所有键值对一次性重新计算位置,搬到新家,然后再恢复服务,这在数据量大的时候,服务得暂停好几秒,简直是灾难。
Redis用的是一种叫“渐进式rehash”的聪明办法,它准备两个“家”(两个哈希表),平时只用第一个,需要扩容时,它开始把第二个家建得更大,但并不急着一次性搬完,而是每当有客户端来执行一个命令(比如读或写一个键)时,它除了处理这个命令,还“顺手”从第一个家里搬那么一两个键值对到第二个新家,这样,搬家的活被分摊到了无数个客户端的请求中,每个请求只多了一点点负担,用户根本感觉不到卡顿。
在这个过程中,查找键值会同时查两个表,新写入的键则直接放到新表,等到某个时候,旧表里的东西都搬空了,就把旧表扔掉,把新表设为当前使用的表,这个设计完美体现了Redis如何将一个大任务“化整为零”,保证服务不间断,细节里全是智慧。

第三幕:事件循环的“时间管理大师”
Redis是单线程的,但它怎么能同时处理那么多网络连接和定时任务呢?秘诀在于它的事件循环(Event Loop)。(来源:ae.c, server.c)
你可以把这个单线程想象成一个非常高效的公司前台,它主要干两件事:1. 盯着大门口(网络连接),看有没有新的快递(客户端请求)送来,或者之前的快递员有没有回复,2. 处理已经收到的快递(执行命令)。
但公司里还有些内部事务需要定时处理,比如每100毫秒检查一下有没有过期的键,这个前台怎么兼顾呢?它不会傻等,它在处理完手头的事情后,会看一眼最近的一个定时任务还要多久才需要执行,比如还有50毫秒,它就在大门口“等待”快递员上门,但最多只等50毫秒,50毫秒一到,就算没快递来,它也会先起身去把检查过期键这个定时任务给做了,做完再回来继续等快递。
这个“等待”的机制,底层用的是像epoll这样的系统调用,非常高效,通过精准地计算等待时间,这个单线程前台既能及时响应外部的网络请求,又能保证内部的定时任务不被耽误,像个真正的“时间管理大师”。

第四幕:持久化时“偷懒”的写时复制
当Redis需要做RDB快照(把内存数据完整备份到磁盘)时,如果数据量很大,备份过程会很长,这期间如果有新的写命令进来,数据变了怎么办?(来源:rdb.c, 涉及操作系统COW机制)
Redis利用了操作系统的一个叫“写时复制”(Copy-on-Write)的特性,当它fork出一个子进程来专门生成RDB文件时,子进程和父进程(主进程)看到的是同一份内存数据,但操作系统会把这些内存标记为“只读”,如果这段时间主进程没有收到任何写命令,那么子进程就安安静静地把数据写入磁盘,相安无事。
妙就妙在,如果此时有个客户端发来了一个命令,要修改某个键的值,主进程会察觉到这块内存是“只读”的,于是操作系统会先真正地把这块内存数据复制一份副本出来,让主进程去修改这个副本,而子进程看到的,还是那个 untouched 的旧数据。
这样做的好处是:1. 速度极快,因为fork子进程几乎不耗时间,而且大部分情况下不需要复制整个内存,2. 保证快照的一致性,RDB文件保存的是fork那个时间点的数据视图,就像拍了一张瞬间的照片,这个“偷懒”的机制,极大地减少了持久化对性能的影响。
通过看这些源码细节,你会发现Redis的高性能和高可靠性不是凭空而来的,而是通过无数个这样精打细算、深思熟虑的设计点堆积起来的,每一个细节都体现了开发者对计算机系统深入的理解和极致的优化精神。
本文由凤伟才于2026-01-18发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:https://haoid.cn/wenda/83297.html
