写点什么

Redis 进阶之路:深度解析 Redis 单线程架构,图文并茂不能再清晰了

作者:王小凡
  • 2022 年 8 月 02 日
  • 本文字数:1918 字

    阅读完需:约 6 分钟

Redis进阶之路:深度解析Redis单线程架构,图文并茂不能再清晰了

众所周知,Redis 是一个单线程架构的 NoSQL 数据库,但是是单线程模型的 Redis 为什么性能如此之高?这就是我们接下来要探究学习的内容。

1、Redis 的单线程架构

1.1、Redis 单线程简介

首先要明白,Redis 的单线程指的是执行命令时的单线程。

Redis 客户端与服务端的模型可以简化成下图,每次客户端调用都经历了发送命令、执行命令、返回结果三个过程。

我们说的单线程就是在第二步执行命令,一条命令从从客户端达到服务端不会立刻被执行,而是会进入一个队列中等待,每次只会有一条指令被选中执行。

发送命令、返回结果、命令排队这些就不是那么简单了,例如 Redis 使用了 I/O 多路复用技术来解决 I/O 的问题。

1.2、Redis 为什么要使用单线程

这是官方的解释:

官方 FAQ 表示,因为 Redis 是基于内存的操作,CPU 成为 Redis 的瓶颈的情况很少见,Redis 的瓶颈最有可能是内存的大小或者网络限制。

如果想要最大程度利用 CPU,可以在一台机器上启动多个 Redis 实例。

值得一提的,网络上存在这样的观点:吐槽官方的解释有些敷衍,其实就是历史原因,开发者嫌多线程麻烦,后来这个 CPU 的利用问题就被抛给了使用者。

同时 FAQ 里还提到了, Redis 4.0 之后开始变成多线程,除了主线程外,它也有后台线程在处理一些较为缓慢的操作,例如清理脏数据、无用连接的释放、大 Key 的删除等等。

1.3、为什么单线程还能这么快

通常来讲,单线程处理能力要比多线程差,那么为什么 Redis 使用单线程模型会达到每秒万级别的处理能力呢?可以将其归结为三点:

1.第一:纯内存访问,Redis 将所有数据放在内存中,内存的响应时长大约为 100 纳秒,这是 Redis 达到每秒万级别访问的最重要的基础。

2.第二:非阻塞 I/O,Redis 使用 epoll 作为 I/O 多路复用技术的实现,再加上 Redis 自身的事件处理模型将 epoll 中的连接、读写、关闭都转换为事件,不在网络 I/O 上浪费过多的时间

这里再扩展一下 I/O 多路复用:

引用知乎上一个高赞的回答来解释什么是 I/O 多路复用。假设你是一个老师,让 30 个学生解答一道题目,然后检查学生做得是否正确,你有下面几个选择:

1.第一种选择:按顺序逐个检查,先检查 A,然后是 B,之后是 C、D。。。这中间如果有一个学生卡主,全班都会被耽误。这种模式就好比,你用循环挨个处理 socket,根本不具有并发能力。

2.第二种选择:你创建 30 个分身,每个分身检查一个学生的答案是否正确。 这种类似于为每一个用户创建一个进程或者线程处理连接。

3.第三种选择,你站在讲台上等,谁解答完谁举手。这时 C、D 举手,表示他们解答问题完毕,你下去依次检查 C、D 的答案,然后继续回到讲台上等。此时 E、A 又举手,然后去处理 E 和 A。

第一种就是阻塞 IO 模型,第三种就是 I/O 复用模型,Linux 下的 select、poll 和 epoll 就是干这个的。将用户 socket 对应的 fd 注册进 epoll,然后 epoll 帮你监听哪些 socket 上有消息到达,这样就避免了大量的无用操作。此时的 socket 应该采用非阻塞模式。


这样,整个过程只在调用 select、poll、epoll 这些调用的时候才会阻塞,收发客户消息是不会阻塞的,整个进程或者线程就被充分利用起来,这就是事件驱动,所谓的 reactor 模式。

第三:单线程避免了线程切换和竞态产生的消耗。

我们继续来看 Redis 单线程却很快的最后一条原因,在多线程开发中,存在线程的切换和竞争,这样一来,是有时间的消耗的。对于需要磁盘 I/O 的程序来讲,磁盘 I/O 是一个比较耗时的操作,所以对于需要进行磁盘 I/O 的程序,我们可以使用多线程,在某个线程进行 I/O 时,CPU 切换到当前程序的其他线程执行,以此减少 CPU 的等待时间。


那么问题来了。Redis 的数据存放在内存中,将内存中的数据读入 CPU 时,CPU 不是依然需要等待吗,为什么不能在等待数据从内存读入 CPU 期间执行其他线程,以此提高 CPU 的使用率呢?这个问题的答案很单,内存的读写速度虽然比 CPU 慢很多,但是也是非常快的。CPU 切换线程需要花费一定的时间,而多次切换线程所花费的时间,可能比直接使用单线程执行相同的任务,花费的时间要更多,这是非常不划算的。

单线程也会有一个问题:对于每个命令的执行时间是有要求的。如果某个命令执行过长,会造成其他命令的阻塞,对于 Redis 这种高性能的服务来说是致命的,所以 Redis 是面向快速执行场景的数据库。

2、支持多线程的 Redis6.0

“Redis 不是单线程吗?怎么又支持多线程了?”

相信学到了这里,这已经不是一个问题了。


Redis6.0 引入了多线程的特性,这个多线程是在哪里呢?——是对处理网络请求过程采用了多线程


Redis 6.0 采用多个 IO 线程来处理网络请求,网络请求的解析可以由其他线程完成,然后把解析后的请求交由主线程进行实际的内存读写。提升网络请求处理的并行度,进而提升整体性能。

那么多并发的线程安全问题存在吗?——当然不存在。

Redis 得多 IO 线程只是用来处理网络请求的,对于命令的执行,Redis 仍然使用单线程来处理。

用户头像

王小凡

关注

还未添加个人签名 2022.07.26 加入

还未添加个人简介

评论

发布
暂无评论
Redis进阶之路:深度解析Redis单线程架构,图文并茂不能再清晰了_Java_王小凡_InfoQ写作社区