Redis 线程模型:单线程也能快如闪电?

目录

🌟我的其他文章也讲解的比较有趣😁,如果喜欢博主的讲解方式,可以多多支持一下,感谢🤗!
🌟了解 缓存雪崩、穿透、击穿 请看 : 缓存雪崩、穿透、击穿:别让你的数据库"压力山大"!

其他优质专栏: 【🎇SpringBoot】【🎉多线程】【🎨Redis】【✨设计模式专栏(已完结)】...等

如果喜欢作者的讲解方式,可以点赞收藏加关注,你的支持就是我的动力

✨更多文章请看个人主页: 码熔burning

一、核心思想:快刀斩乱麻的"单线程"高手 🦸‍♂️

最重要的一点,也是经常被提到的:Redis 在处理客户端的命令请求时,主要是"单线程"的。

啥意思呢?就是说,同一时间,Redis 服务器里只有一个"主心骨"线程在真正地执行我们发过去的 SET, GET, LPUSH 这些命令。

你可以想象成一个速度极快的超级客服 🧑‍💻。

  • 有很多客户(客户端连接)打电话进来咨询或办理业务(发送命令)。
  • 但柜台后面,真正处理这些业务逻辑的,只有这一个超级客服。
  • 他动作飞快 ⚡️,处理完一个客户的请求,马上就接下一个,几乎没有停顿。

二、为什么是"单线程"?🤔

你可能会问,现在电脑 CPU 都是多核的,用单线程不是浪费资源吗?为什么 Redis 不用多线程来处理命令呢?

主要原因有几个:

  1. 避免"锁"的麻烦 🙅‍♂️🔒: 多线程一起操作数据,最怕的就是数据被改乱了。比如两个线程同时修改一个计数器,结果可能就不对了。为了解决这个问题,通常需要用到"锁",但加锁、解锁本身是有开销的,还会让线程互相等待,可能反而降低效率,而且会让代码变得复杂。Redis 单线程处理命令,天然就避免了这种并发修改数据的问题,不需要加锁,代码简单,效率高。
  2. 减少线程切换开销 💨: 多线程运行时,操作系统需要在不同线程之间切换执行权(上下文切换),这个切换本身是需要消耗时间和资源的。单线程就没这个烦恼。
  3. CPU 不是瓶颈 🚀: 对于 Redis 来说,绝大多数操作都是在内存 里完成的,内存读写速度极快。通常情况下,限制 Redis 性能的瓶颈往往是网络 I/O 🌐(接收请求、发送响应)或者内存大小 💾,而不是 CPU 的计算能力。单线程已经足够快到让网络或内存先达到极限了。

三、单线程如何做到高性能?✨ "I/O 多路复用"是关键!

既然只有一个客服(主线程),怎么能同时应付那么多客户(连接)呢?难道一个客户办理业务时,其他客户就只能干等着吗?

当然不是!这里的关键技术叫做 I/O 多路复用 (I/O Multiplexing)

继续用客服的例子来解释:

  • 这位超级客服面前不是只有一个电话,而是一个巨大的总机面板 (这就是 I/O 多路复用机制,比如 Linux 上的 epoll, macOS 上的 kqueue, 或者 select)。
  • 这个总机面板能同时监控所有打进来的电话线路(网络连接)。
  • 客服不需要一个一个拿起电话问:"喂?你要干啥?" 而是看着面板。
  • 只有当某个电话线路真正有事(比如客户说完了请求,或者客服准备好的回复可以发出去了)时,面板上的灯才会亮 💡。
  • 客服只处理那些亮灯的线路 。他快速接起一个亮灯电话,听完请求(读取数据),如果这个请求需要一点时间思考(虽然 Redis 很少有这种),他会先记下来,然后立刻去看面板上还有没有其他亮灯的线路。
  • 如果某个请求处理完了,需要回复客户(发送数据),客服也不会傻等网络把话说完,他把回复内容交给"发件部门"(操作系统内核),然后立刻又回去看总机面板。

总结一下 I/O 多路复用的好处:

  • 一个线程管多个连接:单线程可以同时监控大量的网络连接。
  • 只在"有事"时才处理 :只在连接真正准备好读或写的时候才去操作它,避免了无谓的等待(这叫非阻塞 I/O 👍)。
  • 效率高:大大减少了线程因为等待网络传输而闲置的时间。

所以,Redis 的高性能公式 ≈ 单线程命令处理 + I/O 多路复用 + 基于内存的操作 💪

四、真的一直都只有"一个线程"吗?并不完全是!👻

虽然我们说 Redis 处理命令是单线程的,但这并不意味着 Redis 整个程序里只有一个线程。Redis 在背后还是有一些"帮手"线程的:

  1. 后台持久化 (Persistence) 💾: 当你需要把内存里的数据保存到硬盘上时(比如执行 BGSAVE 生成 RDB 快照,或者 AOF 文件重写),这些操作通常比较慢,会阻塞主线程。为了不影响主线程处理命令,Redis 会fork() 一个子进程 (或者在某些 AOF 模式下使用后台线程) 来专门负责这些慢操作。主线程告诉它:"你去把数据存一下",然后就继续接待客户了。

  2. 异步删除 (Lazy Freeing) 🗑️: 当你要删除一个很大的键(比如一个包含几百万个元素的 List 或 Hash)时,释放内存也可能需要一些时间。如果让主线程慢慢删,也会卡住。Redis 4.0 引入了 Lazy Freeing 机制,主线程只需要把这个大键标记为"待删除",然后交给一个后台线程慢慢地在后台释放内存。主线程可以立刻返回,继续处理其他命令。

  3. 网络 I/O 多线程 (Redis 6.0+ 新增) 🔥: 前面说到,Redis 的瓶颈常常在网络 I/O。当网络连接非常多,数据传输量非常大时,单线程处理网络读写也可能成为瓶颈。为了解决这个问题,Redis 6.0 引入了多线程来处理网络 I/O

    • 解释: 就像超级客服太忙,光是接电话、递回执(读写网络数据)都忙不过来了。于是老板(Redis 作者)给他配了几个助手 🧑‍🤝‍🧑 (I/O 线程)。
    • 这些助手专门负责从网络读数据 📥 和向网络写数据 📤。他们把读到的请求交给客服(主线程),客服处理完后,把回复交给助手,助手再发给客户。
    • 关键点: ✅ 真正的命令执行 (比如计算、修改数据)仍然是由那个单线程的超级客服来完成的,这保证了核心逻辑的简单性和无锁特性。助手们只是分担了网络沟通的压力。
    • 这个功能默认是关闭的,需要配置开启 (io-threadsio-threads-do-reads 配置项)。

五、总结 📝

  • Redis 处理客户端命令的核心部分是单线程的。这让它避免了锁竞争 🙅‍♂️🔒 和上下文切换 💨 的开销,代码也更简单。
  • 它之所以单线程还很快,是因为:
    • 绝大部分操作基于内存 💾,速度飞快 🚀。
    • 使用了I/O 多路复用 ✨ 和非阻塞 I/O 👍,高效地处理大量并发连接,避免了无谓等待。
  • Redis 并非只有一个线程 👻,它有后台线程/进程负责 RDB/AOF 持久化 💾、大键的异步删除 🗑️ 等慢操作,避免阻塞主线程。
  • Redis 6.0 及以后版本 🔥,引入了可选的 I/O 多线程 🧑‍🤝‍🧑,让多个线程可以分担网络数据的读写压力 📥📤,进一步提升在高并发网络负载下的性能,但命令执行本身依旧是单线程 ✅。
相关推荐
西元.1 小时前
详解 Redis repl_backlog_buffer(如何判断增量同步)
数据库·redis·缓存
老华带你飞2 小时前
木里风景文化|基于Java+vue的木里风景文化管理平台的设计与实现(源码+数据库+文档)
java·数据库·vue.js·毕业设计·论文·风景·木里风景文化管理平台
liang89992 小时前
Shiro学习(四):Shiro对Session的处理和缓存
java·学习·缓存
睡睡怪2 小时前
Mysql入门
数据库·mysql·oracle
java_heartLake2 小时前
PostgreSQL 16深度解析(从16.0-16.8)
数据库·postgresql
Themberfue5 小时前
SQL ②-库操作 | 数据类型
数据库·sql·mysql
li_Michael_li5 小时前
MySQL Explain 分析 SQL 执行计划
数据库·sql·mysql
tjsoft5 小时前
Nginx之https重定向为http
数据库
Yan-英杰5 小时前
【百日精通JAVA | SQL篇 | 第四篇】约束
java·服务器·开发语言·数据库·人工智能·sql·mysql
小小码农很少烦恼6 小时前
Issar 搜索
数据库