高性能IO模型:为什么单线程Redis能那么快?
2022-08-09 14:27:29 TwelveT 307
Redis是单线程的吗Redis是单线程,主要是指Redis的网络IO和键值对读写是由一个线程来完成的,这也是Redis对外提供键值存储服务的主要流程。Redis的持久化、异步删除、集群数据同步等功能是由其他线程而不是主线程来执行的,所以严格来说,Redis并不是单线程。Redis为什么用单线程多线程会有额外开销和共享...
Redis是单线程的吗
- Redis 是单线程,主要是指 Redis 的网络 IO 和键值对读写是由一个线程来完成的,这也是 Redis 对外提供键值存储服务的主要流程。
- Redis 的持久化、异步删除、集群数据同步等功能是由其他线程而不是主线程来执行的,所以严格来说,Redis 并不是单线程。
Redis 为什么用单线程
多线程会有额外开销和共享资源的并发访问控制问题,为了避免这些问题,Redis 采用了单线程的模式,而且采用单线程对于 Redis 内部实现的复杂度大大降低单线程 Redis 为什么那么快
- 一方面,Redis 的大部分操作是在内存上完成的,再加上它采用了高效的数据结构,例如哈希表和跳表,这是它实现高性能的一个重要原因。
- 另一方面,就是 Redis 采用了多路复用机制,使其在网络 IO 操作中能并发处理大量的客户端请求,实现高吞吐率。
- redis单线程的高性能 = 内存操作 + 高效的数据结构 + 多路复用机制
网络操作的基本 IO 模型和潜在的阻塞点
首先,我们要弄明白网络操作的基本 IO 模型和潜在的阻塞点。毕竟,Redis 采用单线程进行 IO,如果线程被阻塞了,就无法进行多路复用了。 以 Get 请求为例,需要经过这么几个过程:- 和客户端建立连接(accept),如果连接建立不成功会一直阻塞
- 从 socket 中读取请求(recv),如果数据一直没有到达会一直阻塞
- 解析客户端发送请求(parse)
- 根据请求类型读取键值数据(get)
- 最后给客户端返回结果,即向 socket 中写回数据(send)。
非阻塞模式
Socket 网络模型的非阻塞模式设置,主要体现在三个关键的函数调用上,如果想要使用 socket 非阻塞模式,就必须要了解这三个函数的调用返回类型和设置模式。接下来,我们 就重点学习下它们。在 socket 模型中,不同操作调用后会返回不同的套接字类型。socket() 方法会返回主动套 接字,然后调用 listen() 方法,将主动套接字转化为监听套接字,此时,可以监听来自客户 端的连接请求。最后,调用 accept() 方法接收到达的客户端连接,并返回已连接套接字。

针对监听套接字,我们可以设置非阻塞模式:当 Redis 调用 accept() 但一直未有连接请求 到达时,Redis 线程可以返回处理其他操作,而不用一直等待。但是,你要注意的是,调 用 accept() 时,已经存在监听套接字了。
虽然 Redis 线程可以不用继续等待,但是总得有机制继续在监听套接字上等待后续连接请 求,并在有请求时通知 Redis。 类似的,我们也可以针对已连接套接字设置非阻塞模式:Redis 调用 recv() 后,如果已连 接套接字上一直没有数据到达,Redis 线程同样可以返回处理其他操作。我们也需要有机 制继续监听该已连接套接字,并在有数据达到时通知 Redis。
这样才能保证 Redis 线程,既不会像基本 IO 模型中一直在阻塞点等待,也不会导致 Redis 无法处理实际到达的连接请求或数据。 到此,Linux 中的 IO 多路复用机制就要登场了。
Redis基于多路复用的高性能 I/O 模型

- 多路 Socket。
- IO 多路复用程序。
- 事件队列。
- 文件事件分派器。
- 多类型事件处理器。
其中 IO 多路复用程序,监听多个 Socket。
以一个 Socket 的多种事件类型举例说明。

1 建立连接事件
客户端向服务端建立连接,IO 多路复用监听到建立连接的请求事件后,将请求事件写入队列,文件事件分派器从队列中获取请求事件,交给连接应答处理器处理。连接应答处理器与客户端创建一个 Socket。
2 写请求事件
客户端发送一个 set key value 的请求,IO 多路复用监听到写事件后,将事件写入队列,文件事件分派器从队列中获取请求事件,交给命令请求处理器处理。命令请求处理器在内存实现 set key value 的操作。
3 返回结果事件
客户端准备好收到结果,IO 多路复用监听到返回结果事件后,将事件写入队列,文件事件分派器从队列中获取请求事件,交给命令回复处理器处理。命令回复处理器返回结果。
Redis 6.0 开始引入多线程?
Redis 的多线程部分只是用来处理网络数据的读写和协议解析,执行命令仍然是单线程。 之所以这么设计是不想 Redis 因为多线程而变得复杂,需要去控制 key、lua、事务、LPUSH/LPOP 等等的并发问题。系统的性能瓶颈也主要在网络 I/O 操作上;而 Redis 引入多线程操作也是出于性能上的考虑,对于一些大键值对的删除操作,通过多线程非阻塞地释放内存空间(释放操作不会阻塞网络IO读写,因为网络 IO 读写与释放的命令执行不是同一个线程)也能减少对 Redis 主线程阻塞的时间,提高执行的效率。
免责声明
本站提供的一切软件、教程和内容信息仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络收集整理,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。如果您喜欢该程序和内容,请支持正版,购买注册,得到更好的正版服务。我们非常重视版权问题,如有侵权请邮件与我们联系处理。敬请谅解!
![]() |
相关文章
-
08-09Redis 渐进式 rehash全局哈希表
-
07-21zookeeper实现分布式可重入锁原理
-
06-29HashMap 底层是如何实现的
-
05-25【JUC】循环屏障CyclicBarrier
-
05-24【JUC】CompletableFuture 异步编排详解
-
04-25【JUC】java并发之AQS详解
-
03-17【JUC】信号灯Semaphore 使用及原理
-
03-09redis实现分布式可重入锁
-
02-26微服务分布式事务如何实现
-
02-26如何实现接口的幂等性,多次操作结果均为一致