《redis》4-redis是单线程?

江湖传言,redis是单线程的,习惯了多线程高并发的高大上技术架构之后,猛然回头,发现支持三高的redis是单线程的,这个你能信?

“单线程”描述redis固然不准确,我们只能说,redis在访问存储部分的时候是单线程的。

学习过tomcat的应该清楚,tomcat跟redis的架构有些类似。

tomcat有个专门处理connector的连接器,可以同时处理N个请求,但是connector在提交请求到容器部分的时候,提交的其实是runner,tomcat的自己的线程池会持续处理提交的runner,执行结果返回给response。这也是为什么tomcat能够并发处理多请求的基础。

redis的基础模块中,

我们也总结道redis存在一个访问模块,在我们的实际环境中,

  1. redis服务处理socket请求,多台机器同时请求redis服务,也是跟tomcat一样,使用多路复用IO模型
  2. 请求被提交到操作模块的时候,会把请求抽象成task,放入请求队列,但是只有一个线程处理task。也就是说,生产者消费者模式中,只有一个消费者。跟tomcat的线程池的多线程的区别就在这里
  3. 把请求的结果返回给response

这个时候我们其实可以关注三个点

  1. 处理访问请求的I/O模型-多路复用
  2. 生产者消费者模式
  3. 单线程和线程池的优缺点

I/O模型,就是操作系统处理I/O硬件读写的模型,主要有5中

  1. 同步阻塞操作模型
  2. 同步非阻塞操作模型
  3. 多路复用模型
  4. 异步模型
  5. 信号驱动模型

现在java中多使用多路复用模型,即Redis中使用的模型。他们的主要区别就是,在用户线程(redis的访问模块中的请求处理线程或者tomcat中connector中的请求处理线程),在处理socket的内容时的是否同步和是否阻塞。

简单的同步阻塞模型,比如我们需要读取socket的请求内容,这么一个简单的操作,用户线程需要等待内核线程把数据从io外部设备copy到内核,然后再从内核copy到用户线程空间,这样的速度是缓慢的,用户线程需要同步等待并阻塞,直到这一整个过程结束。

在学习tomcat的时候,李号双老师总结了一下上面的4种IO模型。我们比较关注多路复用模型。

上面是简单的多路复用模型,在用户线程中,

  1. 有一个单独的线程在循环中调用select方法,查找socket中已经做了数据准备的链接
  2. select方法查到准备好的socket,然后经过一些列操作,把socket的操作提取成task,放入队列
  3. 用户另外的线程,处理task队列中的task。

多路复用的特点就是可以处理很多socket。

上面的添加task和处理task的过程就是生产者和消费者模式

那我们在redis中的消费者,即执行task的线程只有一个线程,所以redis是单线程。tomcat却有一个线程池,线程池中有多个线程一起消费task列表。

为什么tomcat和redis选择了不同的路线呢?这跟tomcat和redis处理的业务场景有关,tomcat在web服务中,很多请求需要请求数据库,文件服务器等,这些又是一些外部设备,操作时间长,cpu利用率低,所以可以通过增加多线程,从而达到增加吞吐量。

优点:但是redis是内存存储服务,只需要访问内存,而且大部分内容访问时间复杂度是O(1),所以处理访问的时间非常短,不需要线程等待太长时间,单个线程还可以避免多线程并发造成线程争用和死锁问题,还可以避免线程上下文切换造成的时间消耗。

缺点:但是单线程处理也有个问题,如果task队列中出现一个复杂访问,比如说访问时间是1s,那么队列后面的任务都要阻塞到该任务执行完。

关于其他线程:redis不仅有内存访问模块一个功能,还有主从同步的功能,也有单独的线程。持久化的时候,也有单独的线程。这些功能性的线程也一起为redis的高可用提供的了支撑

猜你喜欢

转载自blog.csdn.net/David_lou/article/details/109002165