Redis源码深挖加实战操作,帮你突破那些难啃的技术难题
- 问答
- 2026-01-17 21:04:38
- 3
Redis为什么这么快?从源码角度看内存分配(jemalloc)
很多人学Redis,第一个听到的特性就是“快”,为什么快?大家都会说:因为它是内存数据库、单线程避免了上下文切换、用了IO多路复用,这些都对,但有一个底层细节常常被忽略,就是内存分配器。
Redis早期使用glibc的malloc,但性能不佳,后来换成了jemalloc,为什么?我们不看复杂的源码,但可以理解其核心思想,想象一下,你的房间(服务器内存)里要放很多不同大小的箱子(数据),glibc的malloc像个随意的管家,你每次要个箱子,它就随便找块空地给你切一块,时间一长,房间里就会有很多切剩的、用不上的小碎片(内存碎片),你再想要个大箱子时,虽然总空间够,但没有连续的空地,管家就只能先进行大扫除(内存整理),这非常耗时。
而jemalloc是个聪明的管家,它事先把房间划分成几个固定的区域,专门放小号、中号、大号的箱子,专门有个区域只放8字节、16字节、32字节…这种小物件,当你需要存放一个10字节的数据时,jemalloc不会给你切个10字节的怪尺寸,而是直接给你一个16字节的“标准箱”,虽然有点浪费(内部碎片),但好处是,这个16字节的箱子释放后,可以完美地给下一个需要8-16字节的数据复用,由于尺寸固定,分配和回收的速度极快,而且极大地减少了内存碎片。

实战操作: 你可以通过Redis的INFO memory命令查看内存使用情况,关注mem_fragmentation_ratio(内存碎片率)这个指标,如果这个值远大于1.5(比如大于2),说明碎片化比较严重了,这时候,重启Redis或者升级到更高版本(新版Redis有主动碎片整理功能)可能是解决办法,理解了jemalloc的原理,你就明白这个指标波动的深层原因了。
第二部分:“单线程”的误解与网络IO模型(ae事件循环)
“Redis是单线程的。” 这句话容易让人误解,准确说,处理命令的核心模块是单线程的,这意味着,它用一个线程顺序处理所有客户端的请求,所以不用担心锁的问题,天然原子性。
但单线程怎么应对成千上万的网络连接呢?秘密就在于I/O多路复用技术,Redis自己实现了一个简单的事件库,叫ae(Asynchronous Events),你可以把它想象成一个超级高效的门卫。

这个门卫(单线程)不负责帮客人搬运行李(处理具体业务),他只负责站在大门口,手里拿着一张清单(一个文件描述符集合),清单上列出了所有可能有事发生的房间号(网络连接),他不停地问操作系统:“我清单上的这些房间,有谁的门铃响了(有数据可读)?或者谁的快递到了可以发送了(有数据可写)?” 操作系统(通过epoll、kqueue等机制)会立刻告诉他哪些房间有动静,然后这个门卫才去敲那些有动静的房门,把里面的客人(网络数据)请出来,交给唯一的服务员(命令处理线程)去服务。
这样,一个门卫和一个服务员就轻松管理了整个酒店,避免了为每个客人都配一个服务员的巨大开销(多线程上下文切换),源码中的aeMain函数就是这个事件循环的核心。
实战操作: 当你发现Redis的CPU占用率很高,但实际处理的QPS并不高时,可能是某个命令执行得太慢(比如keys *),阻塞了整个线程,因为单线程模式下,一个慢查询会影响到所有后续命令,这时,你需要用SLOWLOG GET命令查看慢查询日志,找出并优化这些慢命令,理解单线程模型,你就会对这种“一颗老鼠屎坏了一锅粥”的现象有切肤之痛。
第三部分:持久化奥秘之AOF重写(fork与写时复制)

Redis的AOF持久化是记录所有写命令,但时间长了,AOF文件会很大,比如你执行了100次incr count,AOF会记录100条命令,但其实一条set count 100就能代替,所以需要AOF重写来压缩文件。
重写时,Redis会fork出一个子进程来完成这个任务,这里有个关键点:fork出的子进程和父进程(主进程)共享同一份内存数据,你可能会担心,子进程在重写时,主进程还在接收新命令,数据不会乱吗?
这就用到了操作系统的写时复制(Copy-On-Write) 机制,想象一下,父子进程就像两个人同时在读一本公共的书,在子进程刚fork出来时,它和父进程看的是同一本物理书(共享内存页),只要两个人都只读不写,就相安无事,当父进程接收了一个新写命令(比如修改了书中的某一页),操作系统会立刻把这一页复制一份副本给父进程去修改,而子进程依然读着旧的那一页,这样,子进程在重写时,看到的就是fork那一刻的数据快照,它根据这个快照生成最精简的命令序列,写入新的AOF文件,重写完成后,用新文件替换旧文件。
实战操作: AOF重写期间,如果主进程接收到大量写命令,会导致大量的内存页被复制,可能引起内存占用翻倍,如果你发现Redis在触发bgrewriteaof时服务器内存吃紧,甚至被OOM Killer杀掉,就是因为这个原因,解决方案是确保机器有足够的空闲内存,或者控制重写的触发频率(auto-aof-rewrite-percentage和auto-aof-rewrite-min-size),理解了COW,你就知道这个内存波动的根源了。
通过这三个点——内存分配器jemalloc、单线程事件循环ae、持久化中的fork和COW——我们可以看到,Redis的高性能并非魔法,而是巧妙地结合了操作系统提供的底层机制(多路复用、进程、内存管理)和精良的软件设计,直接阅读源码可能困难,但理解这些核心思想,再结合info命令、慢查询日志等实战工具进行观察和调优,就能真正突破那些看似黑盒的技术难题。
本文由革姣丽于2026-01-17发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:https://haoid.cn/wenda/82632.html
