redis多线程问题

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大佬

Documentation | Redis

01_redis7实战教程简介_哔哩哔哩_bilibili

暂无评论

发送评论 编辑评论

|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇