初学Redis之为什么redis是单线程,但是还能那么快?

redis真的是单线程吗?

首先我们得厘清一个事实,我们说的Redis单线程,其实指的是redis的网络IO和键值对是由一个线程执行,这是redis对外提供键值存储服务的主要流程。此外,还有持久化、异步删除、主从复制数据同步等都是主线程fock出的子线程来执行的。但是fock子线程,主线程也是会阻塞的,实例越大,fock阻塞的时间就会越长。

所以严格来说,Redis并不是单线程来完成所有操作的。
redis为什么用单线程?
以redis中的list为例,若有线程1为List做LPUSH操作将list的长度+1,线程2做LPOP操作将list的长度-1,那么为了确保List长度的正确性,就需要将两个线程串行来完成,否则就可能导致取得的list的长度是错误的。这就是多线程模式下的资源共享的并发访问控制问题。

并发访问控制永远都是多线程不可避免的痛,若是没有优秀的系统设计,只是在并发过程中,增加了粗粒度互斥锁,那么即使使用了多线程,那每个线程访问都会等待锁,并行就变为了串行,并没有像理想中的那样提高吞吐率。

为了避免这个问题,redis干脆就直接使用了单线程模式。
但是呢,redis6.0中也提出了多线程设计模式,这其中是采用CPU多核多线程读写客户端操作,从而进一步提高服务的性能,当然这只是针对客户端的读写,而操作命令实际上还是由单线程来操作。

redis单线程为什么会这么快呢?

  1. redis的所有操作都在内存上完成,再加上它有高效的数据结构。
  2. redis采用的是多路复用机制,能使网络I/O流并发处理大量的客户端请求,提高吞吐率。

多路复用机制接下来就要重点理解了。

基本的网络I/O模型包含了客户端与服务端的连接(accept)、监听连接(listen\bind)、获取客户端请求(recv)、解析客户端的请求(parse)、根据请求命令进行操作(get)、返回数据(send)。

这其中,accept、recv是可能存在的阻塞点。当服务端监听到客户端连接,但是迟迟没有连接成功,accept函数就会阻塞,就导致其他客户端无法与服务端进行连接。而若服务端迟迟没有从客户端中读取出具,那么recv也会被阻塞。同样的肯定存在解决方案,那就是网络模型可以设置非阻塞模式。

非阻塞模式

套接字返回套接字类型非阻塞模式效果
socket主动套接字
listen监听套接字accept()非阻塞
accept已连接套接字recv()、send()非阻塞

设置非阻塞模式后,如果redis线程调用accept但是迟迟没有响应,redis线程还可以执行其他操作,线程不会阻塞。虽然不用阻塞等待,但是还是需要机制监听accept并在有数据请求的时候通知redis,要注意的是调用accept()时已经存在监听套接字。

在Linux中,多路复用就是指在一个线程中要同时处理多个I/O流,就是select/epoll机制。基于多路复用机制,在redis中只启动单线程的情况下,该机制允许内核中存在多个监听套接字和多个已连接套接字,由内核一直监听这些套接字,如果有请求到达,则提示通知redis。

为了能收到请求后通知redis,select/epoll提供了基于事件的回调函数。针对不同事件的发生,调用不同处理函数。
在这里插入图片描述
如上图,每个RQ都是之前所说的套接字,在监听到套接字后,触发对应的事件,再将事件放入到事件队列中,redis单线程会一直不断的处理事件队列。在处理事件队列的时候,会根据事件调用对应的处理函数。因为redis会不断的处理事件队列,所以能及时的响应客户端请求,从而提高响应性能。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值