[{"data":1,"prerenderedAt":70},["ShallowReactive",2],{"article-161":3},{"code":4,"msg":5,"data":6,"count":14},200,"查询成功",{"id":7,"title":8,"keywords":9,"description":10,"category_id":11,"content":12,"body_html":13,"thumb_up":14,"clicks":15,"sort":14,"remark":13,"status":16,"is_open":16,"is_deleted":14,"is_top":16,"is_recommend":16,"create_time":17,"update_time":17,"image_id":18,"url":13,"member_id":14,"cate_name":19,"prev":20,"next":23,"tags":26,"words":41,"read_time":42,"comments":14,"cover":43,"relevant":44},161,"Redis 为什么这么快？单线程只是答案的一部分","Redis、I\u002FO 多路复用、单线程、数据结构","Redis 的高性能不是“单线程”三个字就能解释。本文从内存访问、紧凑数据结构、串行命令执行和 I\u002FO 多路复用四层机制出发，讲清每一层解决的问题，并补充 Redis 6 与 Redis 8 的 I\u002FO 线程演进、慢命令风险和一段可直接复述的面试回答。",13,"面试官问：“Redis 为什么这么快？”\n\n很多人的第一反应是：“因为 Redis 是单线程，没有线程切换和锁竞争。”\n\n这句话抓住了一个特点，却没有解释完整。**单线程本身并不会自动带来高性能**：如果一次命令要执行很久，或者线程一直阻塞在某个网络连接上，后面的请求照样会排队。\n\n更完整的答案是：Redis 把主要数据访问放在内存中，用经过优化的编码和数据结构缩短单次命令的执行时间，以串行的核心命令执行路径降低并发控制成本，再借助非阻塞 I\u002FO 与 I\u002FO 多路复用高效管理大量连接。四层机制共同作用，才形成了我们感受到的“快”。\n\n> 本文讨论 Redis Open Source 的典型工作路径。Redis 的线程与数据结构实现会随版本演进；具体部署的吞吐和延迟还会受到命令类型、数据规模、网络、持久化配置、硬件和客户端使用方式影响。\n\n## 先给面试官一个简版答案\n\nRedis 快，首先因为热路径主要访问内存，避开了按请求随机读取磁盘的高成本；其次，它会为不同数据类型选择合适的内部编码和数据结构，让常见操作尽量短小；再次，核心命令通常串行执行，减少了共享数据上的锁竞争和线程切换，也让执行语义更容易保持确定；最后，Redis 使用非阻塞 I\u002FO 和 I\u002FO 多路复用，让一个事件循环可以管理大量连接，只处理已经就绪的事件。\n\n需要补充的是，Redis 并非所有工作都只使用一个线程。后台持久化、异步释放以及网络 I\u002FO 等工作可能使用其他进程或线程。Redis 6 引入了可选的 threaded I\u002FO，Redis 8 又重做了这套实现；但客户端命令准备完成后，仍会进入主线程执行。因此更严谨的说法是：**Redis 采用 mostly single-threaded 的核心命令执行模型，而不是“整个 Redis 只有一个线程”。**\n\n![四层机制分别解决什么问题](https:\u002F\u002Ftp.myong.top\u002Fstorage\u002Fmarkdown\u002F20260716\u002Ff55a58ea0d4811736cdadf8dac00ced8.png)\n\n## 第一层：以内存为主，缩短数据访问路径\n\nRedis 的主要数据集保存在内存中。对常见读写命令来说，访问目标通常不需要像磁盘型数据库那样，先把不在缓存中的数据页从持久化设备读入内存。内存访问延迟显著低于随机磁盘 I\u002FO，这是 Redis 高性能最基础的一层。\n\n但“Redis 在内存里”不能被理解成“Redis 完全不访问磁盘”。Redis 可以通过 RDB 快照、AOF（Append Only File）或二者组合实现持久化；后台生成快照、AOF 写入与重写、操作系统换页都可能影响延迟。官方延迟文档也特别提醒：一旦 Redis 的内存页被换出到 swap，再次访问就会触发慢得多的磁盘 I\u002FO。\n\n所以更准确的表达是：**Redis 把服务请求的主要数据访问路径放在内存中，同时把持久化作为另一条需要单独权衡的路径。**\n\n## 第二层：数据结构与编码，尽量让单次操作短小\n\nRedis 不只是“在内存里放一个大哈希表”。它对 String、List、Hash、Set、Sorted Set 等类型设计了不同的内部表示，并会结合元素数量、元素大小和配置选择更紧凑或更适合操作的数据编码。\n\n这里最容易出现版本过时的说法。\n\n- 当前 Redis 的 List 主要由 **quicklist** 实现。源码把 quicklist 描述为“由 listpack 组成的双向链表”：外层便于从两端和节点间移动，节点内部用紧凑的连续编码存放多个元素。它不是简单地“小时用连续数组，大时切换成普通链表”。\n- Sorted Set 在规模较小时可以使用紧凑的 **listpack** 编码；达到相关阈值后，典型实现会使用哈希表与 **skiplist（跳表）**。哈希表适合按成员查分值，跳表适合按分值有序遍历与范围查询。\n- Hash 等聚合类型也有紧凑编码与哈希表等不同表示。具体转换阈值是配置项和版本细节，面试时没有必要死背一个可能已经变化的数字。\n\n这些设计不只是为了“理论复杂度漂亮”。紧凑编码可以减少指针和对象头等额外内存，也有机会改善 CPU cache locality；而适合操作语义的数据结构，则让查询、插入、删除和范围访问保持合理复杂度。\n\n![List 与 Sorted Set 的典型内部结构](https:\u002F\u002Ftp.myong.top\u002Fstorage\u002Fmarkdown\u002F20260716\u002F367b8a6960619bbd92cf7f364acaf7d3.png)\n\n## 第三层：串行执行核心命令，降低协调成本\n\nRedis 官方把自己的设计称为 **mostly single threaded**：核心客户端请求通常按顺序执行，同一时刻不会让多个工作线程同时修改同一份键空间数据。\n\n这带来三个直接收益：\n\n1. 核心数据操作不必普遍围绕共享状态加锁，减少锁竞争与同步开销。\n2. 不需要在多个命令执行线程之间频繁切换，调度路径更直接。\n3. 命令按顺序执行，原子性语义和内部状态管理更容易保持清晰。\n\n但要把因果关系说对：**单线程的价值是降低协调复杂度，不是“线程越少天然越快”。** 如果执行了时间复杂度高、数据量很大或包含阻塞行为的命令，主线程会被占住，排在后面的请求都会增加延迟。\n\n这也是为什么生产环境要关注 `SLOWLOG`、命令复杂度、大 Key，以及 `KEYS`、超大范围查询、大集合删除等可能占用主线程较久的操作。对于大对象删除，Redis 还提供 `UNLINK` 等方式，把内存回收工作放到后台线程处理。\n\n## 第四层：I\u002FO 多路复用，让一个事件循环管理大量连接\n\n如果 Redis 每接入一个客户端就让主线程阻塞等待数据，那么单线程当然无法服务大量连接。它真正采用的是：**非阻塞 socket + I\u002FO 多路复用 + 事件循环。**\n\n连接建立后，socket 被设置为非阻塞状态，Redis 注册可读、可写等文件事件。事件循环调用操作系统提供的多路复用机制等待；在 Linux 上通常是 `epoll`，其他平台还可能使用 `kqueue`、`event ports` 或 `select`。只有连接真正就绪时，事件循环才调用对应处理函数。\n\n可以把它理解为：Redis 不会挨个问一万个连接“你有数据吗”，也不会停在某个没有数据的连接上等待；操作系统先返回当前已经就绪的连接集合，Redis 再处理这些事件。\n\n![I\u002FO 多路复用的事件循环](https:\u002F\u002Ftp.myong.top\u002Fstorage\u002Fmarkdown\u002F20260716\u002F7e96b2700ab7daa4276dd34ce8d079c4.png)\n\n一条请求的典型主线可以简化为：\n\n1. 多路复用 API 返回就绪的 socket。\n2. Redis 读取数据并解析协议。\n3. 准备好的命令进入核心命令执行路径。\n4. 结果写入客户端输出缓冲区，并在 socket 可写时发送。\n5. 事件循环继续处理下一批就绪事件。\n\nI\u002FO 多路复用解决的是**如何高效管理大量连接**，单线程串行执行解决的是**如何低成本地操作共享数据**。两者不能混为一谈，也缺一不可。\n\n## Redis 6 之后，还是“单线程”吗？\n\n最稳妥的回答是：“要看你说的是哪一部分。”\n\nRedis 很早就会使用后台线程或子进程处理部分慢任务。Redis 6 又加入可选 I\u002FO 线程，帮助处理客户端 socket 的读写；当前配置说明还包括读取、协议解析和写出等工作。Redis 8 的发布说明显示，其 I\u002FO threading 实现再次重构，并且开启 `io-threads` 后，读写都会使用 I\u002FO 线程。\n\n不过在 Redis 8.0 源码中，I\u002FO 线程解析出完整命令后，会把带有待执行命令的客户端放回主线程队列，命令最终由主线程调用 `processCommand()`。因此，“Redis 6 之后网络 I\u002FO 可以多线程化，而核心命令仍由主线程执行”仍可作为面试中的主线，但最好再补一句：**线程实现会演进，Redis 不是一个从头到尾都只有单线程的程序。**\n\n![Redis 请求处理中主线程与 I\u002FO 线程的边界](https:\u002F\u002Ftp.myong.top\u002Fstorage\u002Fmarkdown\u002F20260716\u002F66823d517808b2bc944119f810368335.png)\n\n## 三个常见误区\n\n### 误区一：Redis 快，主要因为单线程\n\n单线程只是减少协调开销。没有内存访问、短小命令和 I\u002FO 多路复用，单线程无法自然变快。\n\n### 误区二：单线程就没有上下文切换\n\nRedis 核心命令执行路径减少了多个命令线程之间的切换，但操作系统仍会调度进程，Redis 也可能运行后台线程或子进程。说“完全没有上下文切换”过于绝对。\n\n### 误区三：Redis 的瓶颈永远不是 CPU\n\n这同样过于绝对。网络、内存带宽、单核 CPU、慢命令、持久化和客户端使用方式都可能成为瓶颈。判断瓶颈要看实际指标，而不是背固定结论。\n\n## 最后，给出一段可直接复述的回答\n\nRedis 快不是单一原因，而是四层机制共同作用。\n\n第一，主要数据访问在内存中，避免了按请求随机读取磁盘的高延迟；第二，Redis 针对不同数据类型使用紧凑编码、哈希表、quicklist、跳表等结构，让常见命令尽量短小；第三，核心命令通常串行执行，减少了共享数据上的锁竞争和多执行线程切换，也让执行语义更简单；第四，Redis 使用非阻塞 I\u002FO 和 I\u002FO 多路复用，一个事件循环可以管理大量连接，只处理已经就绪的 socket。\n\n所以，真正的关键不是“单线程很快”，而是**单次操作足够短，核心执行协调成本低，网络等待又不会阻塞事件循环**。此外，Redis 并非所有工作都单线程：Redis 6 开始支持可选的 I\u002FO 线程，Redis 8 又重做了 I\u002FO threading，但核心命令执行仍以主线程串行为主。\n\n## 参考资料\n\n- [Redis latency monitoring：Single threaded nature of Redis](https:\u002F\u002Fredis.io\u002Fdocs\u002Flatest\u002Foperate\u002Foss_and_stack\u002Fmanagement\u002Foptimization\u002Flatency\u002F)\n- [Redis client handling：non-blocking I\u002FO 与 multiplexing](https:\u002F\u002Fredis.io\u002Fdocs\u002Flatest\u002Fdevelop\u002Freference\u002Fclients\u002F)\n- [Redis persistence：RDB 与 AOF](https:\u002F\u002Fredis.io\u002Fdocs\u002Flatest\u002Foperate\u002Foss_and_stack\u002Fmanagement\u002Fpersistence\u002F)\n- [Redis 源码：事件循环与多路复用实现选择](https:\u002F\u002Fgithub.com\u002Fredis\u002Fredis\u002Fblob\u002Funstable\u002Fsrc\u002Fae.c)\n- [Redis 源码：quicklist 是 listpack 的双向链表](https:\u002F\u002Fgithub.com\u002Fredis\u002Fredis\u002Fblob\u002Funstable\u002Fsrc\u002Fquicklist.c)\n- [Redis 源码：Sorted Set 的 listpack、哈希表与 skiplist](https:\u002F\u002Fgithub.com\u002Fredis\u002Fredis\u002Fblob\u002Funstable\u002Fsrc\u002Ft_zset.c)\n- [Redis 8.0 release notes：新 I\u002FO threading 实现](https:\u002F\u002Fgithub.com\u002Fredis\u002Fredis\u002Fblob\u002F8.0\u002F00-RELEASENOTES)\n- [Redis 8.0 networking.c：I\u002FO 线程与主线程的命令交接](https:\u002F\u002Fgithub.com\u002Fredis\u002Fredis\u002Fblob\u002F8.0\u002Fsrc\u002Fnetworking.c)\n",null,0,3,1,"2026-07-16 00:28:39",271,"Redis",{"id":21,"title":22},160,"一条 UPDATE 语句背后发生了什么？从 Buffer Pool 到两阶段提交",{"id":24,"title":25},162,"什么是哈希算法？从文件校验、密码存储到 Git 与区块链",[27,30,33,36,39],{"id":28,"name":29},170,"技术面试",{"id":31,"name":32},171,"高并发",{"id":34,"name":35},168,"数据库原理",{"id":37,"name":38},138,"后端开发",{"id":40,"name":19},76,4113,10,"https:\u002F\u002Ftp.myong.top\u002Fstorage\u002Farticle\u002F20260716\u002F9a5b0c343b96ecbfaac32c4d3c09c75b.png",[45,50,55,60,65],{"id":46,"title":47,"create_time":48,"description":49},99,"Memcached、MongoDB和Redis的对比区别","2021-04-25 17:37:30","MongoDB 更类似 MySQL，支持字段索引、游标操作，其优势在于查询功能比较强大，擅长查询 JSON 数据，能存储海量数据，但是不支持事务。 Redis 是一个开源（BSD许可）的，内存中的数据结构存储系统，支持多种类型的数据结构，可用作数据库，高速缓存和消息队列代理",{"id":51,"title":52,"create_time":53,"description":54},97,"Redis必知必会的知识点","2021-04-22 00:21:51","Redis 是一个开源的使用 ANSI C 语言编写、遵守 BSD 协议、支持网络、可基于内存、分布式、可选持久性的键值对(Key-Value)存储数据库，并提供多种语言的 API。Redis 通常被称为数据结构服务器，因为值（value）可以是字符串(String)、哈希(Hash)、列表(list)、集合(sets)和有序集合(sorted sets)等类型。",{"id":56,"title":57,"create_time":58,"description":59},118,"Redis MONITOR 命令详解：实时监控你的 Redis 服务器","2025-10-10 17:33:27","在 Redis 数据库管理中，实时监控命令执行情况是排查问题、分析性能的重要手段。本文将深入解析 `redis-cli -a rafeily MONITOR` 命令的功能、用法及注意事项，帮助你更好地利用这一强大工具。",{"id":61,"title":62,"create_time":63,"description":64},119,"Workerman中Redis异步连接的实现与优化","2025-10-14 20:45:00","在高并发网络应用中，数据库操作往往成为性能瓶颈。特别是在基于Workerman这类事件驱动的PHP框架中，传统的同步Redis连接会阻塞主进程，导致服务响应延迟增加。异步Redis连接通过非阻塞I\u002FO操作，可以显著提高应用性能，充分利用Workerman的并发处理能力。本文将详细分析在Workerman环境中实现Redis异步连接的多种方案，并比较各自的优缺点。",{"id":66,"title":67,"create_time":68,"description":69},149,"被误解的 Redis 单线程：为什么它依然是快如闪电的秘密","2026-05-25 00:04:13","一提起 Redis 的单线程，总有人觉得这是浪费服务器资源的老古董设计。但当你真正理解它的内存操作本质和 I\u002FO 多线程的演进后，你会发现这是一个把“简单”和“安全”发挥到极致的精妙权衡。这篇文章聊聊 Redis 单线程的那些过人之处，以及它真正怕什么",1785081778321]