redis4之后负责处理客户端请求的线程是单线程,但是开始使用异步删除。redis6之后开始使用多线程。
redis4之前
Redis 是基于内存操作的,所以它更首先考虑机器的内存或者网络带宽而并非 CPU,所以选择采用单线程。
Redis的网络IO和键值对读写是由一个线程来完成的,Redis在处理客户端的请求时包括获取 (socket 读)、解析、执行、结果返回 (socket 写) 等都由一个顺序串行的主线程处理,这就是所谓的“单线程”。
Redis命令工作线程是单线程的,但是,比如持久化RDB、AOF等是由额外的线程执行的。对于整个Redis来说是多线程的。
如:

大致流程如下:

优点:
- Redis使用I/O多路复用功能来监听多个socket连接客户端,使用一个线程连接来处理多个请求,减少线程切换带来的开销,同时也避免了IO阻塞操作
- 单线程模型可以避免不必要的上下文切换和多线程竞争,也不会导致死锁。
redis4之后
虽然Redis 是基于内存操作的,它应该更首先考虑机器的内存或者网络带宽而并非 CPU,但是对于多核CPU,为了更好利用资源,开始采用多线程。
而且如果继续采用单线程。
执行删除操作时,如图是value值小时,很快执行完毕。

但是当del一个超级大的key对象时,因为是单线程,会堵塞住。这时其他客户端执行时会出现堵塞。
为了解决这个问题,redis4版本出现了异步(async)删除的命令。也就是子线程来处理
unlink key
flushdb async
flushall async
redis之父antirez一直强调”Lazy Redis is better Redis”.
而lazy free的本质就是把某些cost(主要时间复制度,占用主线程cpu时间片)较高删除操作,从redis主线程剥离让bio子线程来处理,极大地减少主线阻塞时间。从而减少删除导致性能和稳定性问题。
影响redis性能三个因素:cpu,内存,网络IO
而最需要考虑的是网络IO。
而在redis6以后出现了多线程,不过只是用来处理网络请求的,命令工作线程依旧是单线程的,redis6以后采用多个IO线程来处理网络请求,提高网络请求处理的并行度。
因为工作线程依旧是单线程的,所以就不需要考虑多线程加锁了
大致流程如下:

文件描述符
在linux里一切皆文件。
每个打开的文件都会被分配一个唯一的文件描述符。文件描述符是一个非负整数,它作为文件或设备的句柄,用于标识和引用这个打开的文件。文件描述符通过系统调用函数进行创建、读取、写入、关闭等操作。
文件描述符在Linux系统中是进程级别的,每个进程都有自己的文件描述符表,记录了当前进程打开的文件描述符及其相关信息。文件描述符类似一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当一个进程终止时,它的文件描述符也会被释放。
通过文件描述符,可以进行对文件的读取、写入、定位等操作,也可以进行进程间的通信,如管道通信或通过网络套接字进行通信。
IO多路复用
客户端请求服务端就是在服务端的Socket文件中写入客户端对应的文件描述符(FileDescriptor),如果有多个客户端同时请求服务端,为每次请求分配一个线程就会比较耗费服务端资源。所有I/O多路复用就是来解决这个问题。
IO多路复用(IO Multiplexing)是一种高效的I/O处理机制,它允许一个进程能够同时监控多个文件描述符(通常是网络套接字)的可读、可写或异常等事件,从而实现对多个I/O操作的同时监听和处理。
- IO就是网络IO,指操作系统层面对数据在内核态和用户态之间的读写操作
- 多路就是多个客户端连接(连接就是套接字描述符,即 socket 或者 channel)
- 复用就是复用多个线程。
在传统的I/O模型中,通常采用阻塞式I/O或非阻塞式I/O来处理多个I/O操作。但是这两种方式都存在一些问题:阻塞式I/O会导致进程阻塞在某个I/O操作上,无法同时处理其他任务;非阻塞式I/O需要不断地轮询文件描述符的状态,造成CPU资源浪费。
而IO多路复用则通过系统调用函数(如select、poll、epoll等)来监听多个文件描述符的状态,一旦有任何一个文件描述符就绪(可读、可写或异常),进程就可以进行相应的I/O操作。这种方式可以将多个I/O操作集中起来,由内核来负责管理和监控,减少了不必要的轮询和资源开销。
可以看看博客IO多路复用机制 – Dreams (tanjy.site)
使用epoll来监听多个socket的读写事件是一种高效的网络编程模式,被称为Reactor模式。通过将socket设置为非阻塞模式,并将其注册到epoll中,可以实现异步的事件驱动机制。
将用户socket对应的文件描述符(FileDescriptor)注册进epoll,然后epoll帮你监听哪些socket上有消息到达,一旦某个socket上有数据到达,epoll会通知程序进行相应的处理,这样充分利用计算资源,提高并发处理能力。这样,整个过程只在调用select,poll,epoll这些调用的时候才会阻塞,收发客户消息是不会阻塞的。
在这个模式下,整个进程或线程不会被阻塞在等待单个socket上,而是通过epoll等待多个socket上的事件。
在单个线程通过记录跟踪每一个Sockek(I/O流)的状态来同时管理多个I/O流,一个服务端进程可以同时处理多个套接字描述符。目的是尽量多的提高服务器的吞吐能力。
注意:
redis之所以这么快,是因为:
- I/O多路复用
- epoll使用
- 基于内存操作
开启配置
打开redis配置文件
vim redis.conf
以下为配置

将以下参数打开注释
- io-threads设置线程个数,官方建议看下图,注意线程数一定要小于机器核数。
- io-threads-do-reads改为yes,表示启动多线程。

参考
GPT大佬


