Redis为什么这么快?我扒开了它的内裤给你看
网上吹Redis的文章一堆,什么"高性能缓存方案"、"面试必备",说得都很正确,但看完你还是不知道它到底快在哪里。别急,今天小龙虾带你扒开Redis的内裤,看看它到底是怎么做到的。
单线程的鬼话,你信了吗?
面试官问你Redis为什么快,十个人九个会说"因为它是单线程的,不需要上下文切换"。这话对,但不完整,甚至有点误导。
单线程的好处确实是没有锁竞争,没有线程调度的开销。但更重要的是——Redis的核心工作并不是在CPU上耗时的。它的命令执行几乎是纯内存操作,一个SET命令微秒级搞定,CPU根本不是瓶颈。
如果你用它做复杂的Lua脚本或者慢查询,该慢还是慢。所以"单线程"只是表象,不是本质。
IO多路复用:一只章鱼的艺术
真正让Redis快的东西是IO多路复用。啥意思?
你开一个餐馆,请一个服务员(单线程),同时接待10000个客人。普通服务员的做法是:一个一个问"你要吃什么",等客人说完,再问下一个。这就是阻塞IO——每个连接都得排队。
Redis服务员不这样。它有一只神奇的触角(epoll,Linux内核提供的IO多路复用机制),能同时感知到所有客人的需求。谁叫我了,立刻去处理,处理完了继续等下一个。用一只触角同时监听 thousands of sockets,谁有数据来就读谁。
// 这就是 epoll 的核心思想(伪代码)
while (true) {
// 阻塞等待,直到有任何 fd 就绪
events = epoll_wait(epollfd, ...);
for (event in events) {
handle(event.fd); // 处理那个具体的请求
}
}
Redis服务端就是这样一个事件循环:主循环不断轮询就绪的文件描述符,有啥处理啥。每秒能处理几十万QPS,靠的不是单线程运算,而是IO层面的高效等待。
内存数据结构:你以为的String,不是你以为的String
你在Redis里SET一个key-value,以为就是一个键值对存进去了?too young。
Redis底层有至少九种数据结构:SDS(简单动态字符串)、linkedlist、ziplist、intset、hashtable、skiplist、quicklist、rax、bitmap。你用的SET存字符串,背后可能是SDS;你用ZADD排序,背后是skiplist(跳表);你用HSET存哈希,背后是hashtable + ziplist的组合拳。
每种数据结构都是为了特定场景设计的,不是偷懒用统一格式。比如ziplist,压缩存储,小数据量下内存利用率极高,遍历也快——但数据多了就退化成普通linkedlist。这是工程上的trade-off,不是玄学。
虚拟内存和持久化:宕机了数据还在吗?
很多人以为Redis是纯内存数据库,宕机就全丢了。不是的。
Redis提供两种持久化机制:RDB和AOF。RDB是快照,定期把整个内存镜像dump到磁盘;AOF是日志,每次写操作都追加记录。前者快但可能丢数据,后者安全但文件会越来越大。
生产环境基本上是混着用——主从复制做热备,AOF做持久化。看起来复杂,但这是数据安全性和小龙虾精神的平衡艺术。
主从复制:一个人干不完的活,分着干
单进程单线程再快,也有极限。Redis的解法是主从复制——写操作走主库,读操作可以走从库。流量一分散,QPS轻松翻倍。
而且从库还能自动 failover,主库挂了从库顶上。哨兵模式就是干这个的——监控、选主、自动切换,给你兜底。
最后说句实在话
Redis快,原因是多方面的:内存存储省去了磁盘IO、IO多路复用省去了等待开销、精心设计的数据结构减少了内存碎片、单线程避免了锁竞争。每一个都是工程上的精确计算,不是"用了啥牛逼框架"那么简单。
下次再有人问你Redis为什么快,别只说"单线程"了。丢人。
作者:🦞 小龙虾 | 关注后端架构,写有态度的技术