Redis线程问题

Redis · 2023-06-22 · 137 人浏览

Redis到底是单线程还是多线程?

这种说法有些不严谨!!! 考虑到版本上的问题

版本3.x最早的版本:redis是单线程!

redis4.x之后才支持多线程,直到redis6/7才稳定

image-20230519014224072

Redis是单线程

​ 主要是指Redis的网络IO和键值对读写是由一个线程来完成的,Redis在处理客户端请求时包括获取(socket读)、解析、执行、内容返回(socket写)等都是由一个顺序串行的主线程处理,这就是所谓的"单线程"。这也是Redis对外提供键值对存储服务的主要流程。

image-20230519014910808

但Redis的其他功能,比如持久化RD、AOF、异步删除、集群数据同步等等,其实是由额外的线程执行的。Redis命令工作线程是单线程的,但是,整个Redis来说,是多线程的;

Redis3.x单线程时代性能依旧快的原因

  1. 基于内存操作:Redis的所有数据都存在内存中,因此所有的运算都是内存级别的,所以它的性能比较高。
  2. 数据结构简单:Redis的数据结构时专门设计的,而这些简单的数据结构的查找和操作的时间大部分复杂度都是O(1),因此性能比较高。
  3. 多路复用和非阻塞IO:Redis使用I/O的多路复用功能来监听第一个Socket连接客户端,这样可以使用一个线程来处理多个请求,减少了线程切换带来开销,同时也避免了I/O阻塞操作。
  4. 避免上下文切换:因为单线程模型,因此就避免了不必要的上下问切换和多线程竞争,这样省去了多线程来回切换带来的时间和性能上的小号,而且单线程不会导致死锁问题的发生。

image-20230519020748445

为什么逐渐加入多线程特性?

  1. 硬件的发展,multiple CPU or cores?

    cup都是多核时代了,redis还是单线程。。。?

  2. 单线程的痛点是什么?不得不修改?----单----多。

痛点:主线程卡顿====举个例子---redis3.x的经典故障。

正常情况下使用del指令可以快的删除数据,但是当删除Key是一个非常大的对象时,例如包括了成千上万个hash集合时,那么del指令就会在成Redis主线程的卡顿。

4.x增加了多线程!就是为了解决删除出数据效率较低的问题--

而lazy free 的本质就是把某些cost(主要时间复制度,占用主线程的CPU时间片)较高的删除操作,

从redis主线程剥离让bio子线程处理减少主线程的阻塞时间。从而减少删除导致性能和稳定性问题!

对于Redis主要的性能瓶颈时是内存或者网络带宽而并非CPU

image-20230519233914192

Redis的瓶颈可以初步定为:网络IO

在Redis6/7中,第一个新特性就是多线程

​ Redis一直被大家熟知的就是它的单线程架构,虽然有些命令操作可以用后台线程或子进程执行〈比如数据删除、快照生成、AOF重写)。但是,从网络IO处理到实际的读写命令处理,都是由单个线程完成的。

​ 随着网络硬件的性能提升,Redis的性能瓶颈有时会出现在网络IO的处理上,也就是说,单个主线程处理网络请求的速度跟不上底层网络硬件的速度.

为了应对这个问题:

​ 采用多个IO线程来处理网络请求,提高网络请求处理的并行度,Redis6/7就是采用的这种方法。

​ Redis的多IO线程只是用来处理网络请求的,对于读写操作命令Redis仍然使用单线程来处理。这是因为,Redis处理请求时,网络处理经常是瓶颈,通过多个IO线程并行处理网络操作,可以提升实例的整体处理性能。而继续使用单线程执行命令操作,就不用为了保证Lua脚本、事务的原子性,额外开发多线程互斥加锁机制了(不管加锁操作处理),这样一来,Redis线程模型实现就简单了

主线程和IO线程是怎么协同完成请求处理的---四个阶段

阶段一:服务端和客户端建立Socket连接,并分配处理线程
首先,主线程负责接收建立连接请求。当有客户端请求和实例建立Socket连接时,主线程会创建和客户端的连接,并把 Socket放入全局等待队列中。紧接着,主线程通过轮询方法把Socket连接分配给IO线程。
阶段二:IO线程读取并解析请求
主线程一旦把Socket分配给IO线程,就会进入阻塞状态,等待IO线程完成客户端请求读取和解析。因为有多个IO线程在并行处理,所以,这个过程很快就可以完成。
阶段三:主线程执行请求操作
等到IO线程解析完请求,主线程还是会以单线程的方式执行这些命令操作。
阶段四:IO线程回写Socket和主线程清空全局队列
当主线程执行完请求操作后,会把需要返回的结果写入缓冲区,然后,主线程会阻塞等待I0O线程,把这些结果回写到;Socket中,并返回给客户端。和IO线程读取和解析请求一样,IO线程回写Socke时,也是有多个线程在并发执行,所以回写Socket的速度也很快。等到IO线程回写Socket完毕,主线程会清空全局队列,等待客户端的后续请求。

image-20230520195021802

Unix网络编程中的五种IO模型

  1. Blocking IO---阻塞IO
  2. NoneBlocking IO---非阻塞IO
  3. IO multiplexing---IO多路复用
  4. signal drven IO---信号驱动IO
  5. asynchronous IO---异步IO

IO多路复用

​ 一种同步的Io模型,实现一个线程监视多个文件句柄,一旦某个文件句柄就绪就能够通知到对应应用程序进行相应的读写操作,没有文件句柄就绪时就会阻塞应用程序,从而释放CPU资源。

epoll函数

image-20230520200304537

只使用一个服务端进程可以同时处理多个套接字符描述连接。

总结:redis为什么这么快

IO多路复用+epoll函数使用,才是Redis为什么这么快的原因,而不是仅仅单线程命令+redis安装在内存中。
从Redis6开始,就新增了多线程的功能来提高IO的读写性能,他的主要实现思路是将主线程的IO读写任务拆分给一组独立的线程去执行,这样就可以使多个socket的读写可以并行化了,采用多路T/0复用技术可以让单个线程高效的处理多个连接请求〈尽量减少网络IO的时间消耗),将最耗时的Socket的读取、请求解析、写入单独外包出去,剩下的命令执行仍然由主线程串行执行并和内存的数据交互。

image-20230520201208281

image-20230520201312492

Redis7默认是否开启多线程?

答案是关闭的!在redis.conf文件中配置

image-20230520201653360

image-20230520201747139

Redis

上一篇 : 分布式锁Mysql篇

下一篇 : Redis中BigKey问题

状态 统计 图床 又拍云提供CDN加速和云存储 阿里云提供服务计算服务

Powered by Typecho • Theme Jasmine