Redis面试题(二):单线程的Redis为什么这么快

在这里插入图片描述

原文地址:Redis面试题(二):单线程的Redis为什么这么快
原文地址:Redis面试题(二):单线程的Redis为什么这么快
原文地址:Redis面试题(二):单线程的Redis为什么这么快
原文地址:Redis面试题(二):单线程的Redis为什么这么快
原文地址:Redis面试题(二):单线程的Redis为什么这么快

预先知识

Redis的IO多路复用
主要组成部分:套接字,I/O多路复用程序,队列,事件分派器,事件处理器
图片

套接字:通常指的是封装了ip和port的结构体。主要对进程间通信或者网络通信的抽象。比如我们建立一个tcp连接,本质上就是两端都有一个套接字,记录了源ip,端口和目的ip,端口。也像一个类,里面有这些属性。一个套接字可以理解成是一个服务端(客户端)

I/O多路复用程序:多路:多个客户端,复用:一个线程。
比如我们模拟服务器处理1024个客户端请求,根据TCP的三次握手请求,服务端和客户端才会成功建立链接,此时存在50个客户端和一个Redis服务端,那么Redis如何快速处理这1024个请求的任务呢?
举例:把客户端比作同学,把服务端比作老师,月考的场景
(同步阻塞,效率最慢)
1024个同学,1个老师顺序收卷,先检查A,再检查B,再检查C…, 若其中有一个未完成,则后续阻塞等待,基本不具备并发能力,效率很慢。

(同步非阻塞)
1024个同学中,遇见未完成的,先跳过,效率优于同步阻塞场景,但是有一个缺点:收了1024次,一个人都没完成。

select/poll:
1024个同学中,写完了可以举手,老师没带眼睛,有点近视,并不知道是哪个同学写完了,只能在1024个学生中依此遍历找举手的这个同学,扫描文件符的时间复杂度为O(n)。

小知识点:select默认只能处理1024个文件符(FD),poll没这个限制,同时这两个函数库其实都会带来用户态到内涵态的数据拷贝的缺点,epoll解决了这些问题,是他们的最终升级版。

epoll(I/O默认的系统调用函数)
1024个同学中,写完了可以举手,老师配了眼镜,知道是谁举手了,不限于1024个同学,扫描文件符的时间复杂度为O(1)。

第三四个例子中的,谁完成了谁先举手,就是事件驱动,这也是典型的Reactor模型,I/O多路复用程序去监听多个套接字,当套接起触发事件(举手),I/O多路复用程序会把这个套接字放入队列,让事件分派器分配对应的处理器进行处理,当处理完一个套接字的全部事件后,才处理下一个套接字的事件。

结论:Redis基于Reactor模式开发了自己的网络事件处理器,被称之为文件事件处理器,由套接字,I/O多路复用程序,文件事件分派器,事件处理器组成
I/O多路复用程序会同时监听很多个套接字,当被监听的套接字准备执行accept,read,write,close等操作时,与操作对应的文件事件就会产生,I/O多路复用程序会将所有产生事件的套接字都压入一个队列中,然后有序地每次仅以一个套接字的方式传送给文件事件分派器,文件事件分派器分给队员的文件事件处理器进行处理。
只有当上一个套接字产生的事件被所关联的事件处理器执行完毕后,I/O多路复用程序才会继续向文件事件分派器分配下一个套接字,所以对每个命令的执行事件有要求,如果某个命令执行时间过长,会造成其他命令的阻塞等待。

Redis是单线程命令,假设有三个客户端同时执行命令,根据咱们的I/O多路复用程序,始终会有一个命令先分派先执行,不会有线程不安全问题,这也是为什么说单命令能保证原子性的原因,说通俗点:单线程执行命令不存在线程不安全问题。

事件处理器
连接应答处理器
当Redis服务器初始化时,Redis的连接应答处理器会和服务器监听套接字的ae_readable套接字进行关联,当有客户端连接服务器时,这个客户端就会产生ae_readable事件,引发连接应答处理器,执行相应的操作。

命令请求处理器
当一个客户端通过连接应答处理器成功连接到服务器后,客户端向服务端发送一个命令请求,服务器会将客户端的ae_readable事件和命令请求处理器关联,引发命令请求处理器处理。

存在疑问1:咋产生的ae_readable事件跟连接应答处理器和命令请求处理器都有关系呢?

命令回复处理器

当客户端准备好接受服务器传回的命令回复时,会触发ae_writeable事件,比如客户端执行查询操作时,服务器会将客户端的ae_writeable事件与命令回复处理器关联,引发命令回复处理器执行,当命令回复处理器将命令回复全部写入到套接字中后,服务器会解除ae_writeable和命令回复处理器的关联。

固一个单命令在执行的时候,无论多少个请求执行这个单命令,单命令触发的事件,处理完了才能处理下一个套接字的事件。

影响Redis的五大因素
全量查询和聚合查询
Redis主要是用处理网络请求和命令执行,在命令执行阶段,是使用单线程处理的,当存在时间复杂度为O(n),会阻塞后续的请求执行。

删除大量数据(bigkey)
删除操作的本质是要释放掉键值对所占用的内存空间,第一步是:释放内存,第二步是把释放掉的内存块以链表的形式建立链接,方便后续管理和再分配,这个过程本身就耗时,如果一下子释放了大量内存,空闲内存块链表的操作时间也会增加,固也会造成Redis阻塞。可优化

清空数据库
同理跟删除大量数据类似,如果涉及到大量的键值对空间的释放,会造成空闲内存块链表的操作时间会增加。

持久化文件同步
Redis唯一进行磁盘I/O的操作,众所周知,CPU速度>内存速度>磁盘速度,同步持久化文件会阻塞Redis。可优化

从库执行持久化文件
主从同步时,采用全量复制/增量复制,子线程会向主线程发送psyn请求建立链接,主库会创建子线程生成rdb文件和传输文件给从库,在全量复制阶段,从库会必须先清空数据库,然后再加载rdb文件,这两个阶段都会导致Redis的从库会被阻塞住。

随着Redis的版本升级,引进了异步处理以上部分操作,异步必须遵循条件
如果一个操作能被异步执行,就意味着它并不是Redis主线程的关键路径。
比如:客户端把请求发送给Redis服务器,必须要等着Redis回复才算结束。
(跟Java中的多线程一样,把无关紧要的操作可以交给多线程处理)

针对于查询而言:查询必须要查询到结果,返回给客户端,固在Redis的关键路径上,无法优化,固尽量的要减少全量查询,时间复杂度是O(n)的操作。

针对于删除大量key/清除数据库而言:客户端并不需要等待结果,固不在Redis的关键路径上,Redis提供了惰性释放大对象的(Lazy Free)后台线程来优化此阶段。

针对于同步操作:为了保证数据的可靠性,Redis需要保证AOF日志中的操作记录落盘,这个操作不回返回具体的数据给客户端,Redis提供(aof_fsync)后台线程来优化AOF的同步。其实aof和rdb都是fork了子线程去处理数据。
关于AOF的配置,这三种写回策略都依赖于操作系统函数:write和fsync。
write是写到缓冲区中就可以返回,并不需要等待日志写回磁盘中
fsync是要等待日志写回磁盘中才结束。
图片

虽然咱们刚刚说,Redis针对于同步已经使用了异步的优化手段,但主线程会监控fsync的执行进度,当第一次执行fsync时,能使用异步,但第二次执行fsync若发现第一次的还未执行完,则会阻塞等待。

针对于从库加载RDB文件:从库需要给客户端提供读服务,固必须要等待Redis从库的RDB全部被加载成功了,才能提供完整的读服务,固无法优化。

原理:在执行同步操作,删除操作,清空数据库时,Redis的主线程会把任务封装进一个队列中,然后返回客户端完成的回复,但此时任务并没有完成,通过队列和子线程进行交互,后台子线程判断任务的操作类型,然后用对应的子线程执行对应的任务。

Redis6.0版本的多线程主要解决什么问题
在了解这个问题时,我们先了解下Redis的前世今生的进化版本。
1:单线程
Redis的请求一般分为两个模块:命令执行模块和网络模块,
我们常说的Redis是单线程也指的是一个请求涉及到的命令执行模块和网络模块都是同一个线程处理的。

2:单线程+多线程(处理非关键路径上的任务)
Redis的多线程采用了同步持久化文件,惰性释放大对象,关闭临时生成的rdb,aof的临时文件,这些都是非关键路径,不用等待结果。

3:单线程+多线程(网络IO+处理非关键路径上的任务)
Redis中的多线程包括了2,同时还新增多线程处理网络IO,因为Redis的瓶颈并不是CPU,而是网络带宽和机器内存,固从网络带宽处优化也是一种性能的提升,由主线程轮训分配网络请求给不同的IO线程,但单线程还是处理命令执行模块。

4:为什么用多线程处理网络请求,命令请求模块为什么不使用多线程呢?
个人理解:使用多线程处理命令请求(读和写),可能涉及到同一时间访问共享资源,带来线程不安全问题,加锁又会带来性能上的消耗,使用单线程处理命令执行模块,可维护性更高,维护成本更低,同时Redis是基于内存操作的,CPU也不是它的瓶颈,使用I/O多路复用程序足以把效能发挥最大。

如何理解:非关键路径上的任务 比如:查询时,需要拿到结果展示在前端,这就是关键路径,针对任务的结果不那么重要时,就可以理解为是非关键路径上的任务,类似于Java中的异步处理任务一样。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值