Redis为什么快?

86 阅读5分钟

Redis为什么快呢?你说单线程所以快?但Redis真的是单线程吗?

单线程就一定快吗?那要多线程干什么呢?

Redis6引入了多线程,提升了IO效率吗?

带着以上连续的追问,我们进入今天的问题

Redis Server并不是纯粹的单线程

Redis 与 MySQL同样是基于网络的存储中间件,我们将其server结构类比

发现两者均需要处理网络连接,解析(对应mysql server分析器),操作(mysql server 优化器,执行器),以及存储到存储模块中(mysql server存储引擎)

IMG_E0F11F136A14-1.jpeg

如果redis server真的是单线程,在执行dump in-memory dataset to permanent disk时,必然要阻塞对外服务,又谈何高可用?

Redis的单线程,指的是Redis的网络IO和键值对读写是由一个线程完成的

这也是Redis对外提供键值对存储服务的主要流程 但Redis的其他必不可少的功能,如持久化(避免断电or OS Crash),异步删除,集群数据同步,都是由额外的线程执行的

为什么用单线程?为什么单线程能这么快?

多线程的开销

为了保证多线程下共享资源的安全,我们需要额外的机制来保证,否则线程不安全将导致不可预期的数据结果, 额外的机制(LOCK)会消耗资源

举例当使用redis list时,假设redis使用多线程设计,同时两个线程对同一个list进行结构修改,为了保证结构的正确性,redis只能使两个操作串行执行,否则就会得到错误的list结构

这就是多线程模型面临的共享资源并发访问控制的问题

也就是说,如果没有精细的设计,包括竞争烈度的考量,锁粒度的考量,简单粗暴使用粗粒度的互斥锁的话,就会出现不理想的结果:即使增加了线程,大部分线程也都处于waiting状态,系统的吞吐量并未增加

采用多线程同样会增加代码的复杂性,减少易调试性和可维护性,为了避免这些问题,redis采用了单线程模式

单线程为什么快?

第一方面是redis采用了高效的数据结构,例如哈希表和跳表

第二方面是Redis采用了多路复用机制,使其在网络IO中能够并发处理大量的客户端请求,实现高吞吐率

什么是多路复用机制?

Linux中的多路复用机制指的是一个线程处理多个IO流,就是常说的select/epoll机制,简单来说,在 Redis 只运行单线程的情况下,该机制允许内核中,同时存在多个监听套接字和已连接套接字。内核会一直监听这些套接字上的连接请求或数据请求。一旦有请求到达,就会交给 Redis 线程处理,这就实现了一个 Redis 线程处理多个 IO 流的效果。Reids在对事件队列中的时间进行处理时,会调用相应的回调函数完成基于事件的回调,所以也可以及时相应客户端请求

单线程和原子性

redis的单个命令都是原子性的,比如字符串拼接,数字递增,push element to a list等 但单线程和原子性没有必然联系 举例:在java 多线程环境下,synchronize修饰的代码块同样的原子性的

Redis单线程模型(结合IO多路复用机制)出现的性能瓶颈

之前我们分析到了,Redis避免并发竞争,多线程切换开销而使用了单线程模型,同时借助操作系统级别的IO多路复用机制,将请求置入队列,无需阻塞,此时出现了瓶颈点:

  1. 一旦队列中得请求发生耗时,都会影响整个server的性能,即后面的请求要等待前面的请求处理完成
  • a、操作bigkey:写入一个bigkey在分配内存时需要消耗更多的时间,同样,删除bigkey释放内存同样会产生耗时;
  • b、使用复杂度过高的命令:例如SORT/SUNION/ZUNIONSTORE,或者O(N)命令,但是N很大,例如lrange key 0 -1一次查询全量数据;
  • c、大量key集中过期:Redis的过期机制也是在主线程中执行的,大量key集中过期会导致处理一个请求时,耗时都在删除过期key,耗时变长;
  • d、淘汰策略:淘汰策略也是在主线程执行的,当内存超过Redis内存上限后,每次写入都需要淘汰一些key,也会造成耗时变长;
  • e、AOF刷盘开启always机制:每次写入都需要把这个操作刷到磁盘,写磁盘的速度远比写内存慢,会拖慢Redis的性能; f、主从全量同步生成RDB:虽然采用fork子进程生成数据快照,但fork这一瞬间也是会阻塞整个线程的,实例越大,阻塞时间越久;
  1. 单线程读写客户端IO存在性能瓶颈

在我之前的文章里 IO基础与Netty基础 - 掘金 (juejin.cn)介绍了5种IO模型

我们说阻塞IO模型、非阻塞IO模型、IO复用模型和信号驱动IO模型都是同步的IO模型。原因是因为,无论以上那种模型,真正的数据拷贝过程,都是同步进行的。

Redis6.0的提升

针对问题1: 需要业务人员去规避,另一方面redis4.0推出了lazy-free机制,把bigkey释放内存的耗时操作放在异步线程中执行,降低对主线程的影响

针对问题2: 一定要明白redis6.0多线程提升了什么!是高并发场景下利用多核多线程读写客户端数据,进一步提升server性能,也仅仅针对客户端的读写是并行的,每个命令的真正操作依旧是单线程的