我们提供安全,免费的手游软件下载!

安卓手机游戏下载_安卓手机软件下载_安卓手机应用免费下载-先锋下载

当前位置: 主页 > 软件教程 > 软件教程

Redis单线程与多线程机制

来源:网络 更新时间:2024-06-05 09:30:43

Redis通常被称为单线程,这是因为Redis的网络IO和键值对读写由一个线程完成,其他功能如持久化、异步删除、集群数据同步则由额外的线程执行。因此,严格来说,Redis并不是单线程运行的。

多线程开发会带来并发控制和资源开销的问题。为了避免这些问题,Redis采用了单线程模式。

Redis单线程模型能够实现每秒处理数十万级别的能力。这一方面得益于大部分操作在内存上完成,以及高效的数据结构,例如哈希表和跳表。另一方面,采用了多路复用机制,使得在网络IO操作中能够并发处理大量的客户端请求,从而实现高吞吐率。

在了解多路复用之前,需要先了解网络操作的基本IO模型和潜在的阻塞点。如果单线程被阻塞,就无法进行多路复用。例如,在Get请求中,bind/listen、accept、recv、parse和send属于网络IO处理,而get则属于键值数据操作。
网络IO操作中的潜在阻塞点分别是accept()和recv()。当Redis监听到一个客户端有连接请求,但一直未能成功建立起连接时,会阻塞在accept()函数这里,导致其他客户端无法和Redis建立连接。类似地,当Redis通过recv()从一个客户端读取数据时,如果数据一直未到达,Redis也会一直阻塞在recv(),这就导致Redis整个线程阻塞,无法处理其他客户端请求,效率很低。不过,Socket 网络模型可以设置非阻塞模式,基于此Linux中的IO多路复用机制就可发挥作用。

Linux中的IO多路复用机制是指一个线程处理多个IO流,即select/epoll机制。在Redis只运行单线程的情况下,该机制允许内核中同时存在多个监听套接字和已连接套接字。内核会一直监听这些套接字上的连接请求或数据请求。一旦有请求到达,就会交给Redis线程处理,实现一个Redis线程处理多个IO流的效果。
图中的多个FD指的是多个套接字,Redis网络框架调用epoll机制,让内核监听这些套接字。此时,Redis线程不会阻塞在某一个特定的监听或已连接套接字上,所以Redis可以同时和多个客户端连接并处理请求,从而提升并发性。

为了在请求到达时能通知到Redis线程,select/epoll提供了基于事件的回调机制,即针对不同事件的发生,调用相应的处理函数。一旦监测到FD上有请求到达时,select/epoll就会触发相应的事件,这些事件会被放进一个事件队列,Redis单线程对该事件队列不断进行处理。这样一来,Redis无需一直轮询是否有请求实际发生,这就可以避免造成CPU资源浪费。同时,Redis在对事件队列中的事件进行处理时,会调用相应的处理函数,这就实现了基于事件的回调。因为Redis一直在对事件队列进行处理,所以能及时响应客户端请求,提升Redis的响应性能。

Redis 6.0之前,虽然有些命令操作可以用后台线程或子进程执行(比如数据删除、快照生成、AOF重写),但是,从网络IO处理到实际的读写命令处理,都是由单个线程完成的,有时会成为Redis的性能瓶颈。Redis 6.0之后采用多个IO线程来处理网络请求,提高网络请求处理的并行度,对于读写命令,仍然使用单线程来处理。

具体流程:
(1)主线程接收到客户端连接请求后创建连接,将Socket放入全局等待队列中,通过轮询分配给IO线程。
(2)分配后主线程就会进入阻塞状态,等待IO线程完成客户端请求读取和解析,多个IO线程在并行处理。
(3)IO线程解析完请求,主线程还是会以单线程的方式执行这些命令操作。
(4)主线程执行完请求操作后,把返回结果写入缓冲区,主线程阻塞等待IO线程把这些结果回写到Socket中,并返回给客户端。
和IO线程读取和解析请求一样,IO线程回写Socket时也是有多个线程在并发执行,所以回写Socket的速度也非常快。等到IO线程回写Socket完毕,主线程会清空全局队列,等待客户端的后续请求。

在实际应用中,如果Redis实例的CPU开销不大,吞吐量却没有提升,可以考虑使用多线程机制提升吞吐量,redis.conf中设置:
1. 设置io-thread-do-reads配置项为yes,表示启用多线程。

io-threads-do-reads yes

2. 设置线程个数要小于Redis实例所在机器的CPU核心数,例如,对于一个8核的机器来说,Redis官方建议配置6个IO线程。

io-threads  6