【面试题】Redis是单线程的为什么那么快

1. 数据在内存中,全部是内存操作

Redis是一个内存数据库,它的数据都存储在内存中,这意味着我们读写数据都是在内存中完成,这个速度是非常快的。

Redis是一个KV内存数据库,它内部构建了一个哈希表,根据指定的KEY访问时,只需要O(1)的时间复杂度就可以找到对应的数据。同时,Redis提供了丰富的数据类型,并使用高效的操作方式进行操作,这些操作都在内存中进行,并不会大量消耗CPU资源,所以速度极快。

HashMap底层查找的时间复杂度?

HashMap的存储结构为数组+链表+红黑树。
理想情况下,哈希不冲突,可以直接找到结果,所以O(1)
在最差的情况下,HashMap保存的数据都在链表中保存,所以需要遍历链表,所以时间复杂度为O(n)。
但是一个好的Hash函数,冲突是比较少的,且HashMap在达到阈值时,还会自动扩展。所以一般情况下,可以认为理想情况下的O(1)就是HashMap查找的时间复杂度。

2. 采用IO多路复用机制

Redis采用单线程,那么它是如何处理多个客户端连接请求呢?

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

那么到底什么是 I/O 多路复用模型,以及在 redis 中怎么实现的。我这里先打几个比方来方便大家理解。

要过年了,老王去火车站买票回家过年,春运期间票不好买,老王买了三天买到了一张退票。这样一个场景老王有三种方式来完成这次买票:
方式一:老王去到火车站售票大厅,在长椅上躺了两夜,终于在第三天等到了一张票,兴高采烈的回家了。(老王在火车站待了三天,其他啥事没干,还耗费了 6 桶泡面一床棉被)
方式二:老王去到火车站售票大厅买票,没买到,之后每天中午再去一次,终于在第三天买到了票。(老王往返车站 6 次,路上耗费了 3 小时,不过这几天其他时间送了三天外卖,又给家里的老婆挣了不少钱)
方式三:老王去到火车站售票大厅买票,没买到,这时候看到一个黄牛在帮别人买票,老王想着还有外卖要送,就让黄牛帮他买,三天后黄牛买到了票通知他下班后来取。(老王往返车站两次,路上耗费 1 小时,给了黄牛 50 块手续费,其他时间送了三天外卖,由于老王临近过年每天都没耽搁的加班送外卖,平台奖励了老王 500 块)

第一种方式就是阻塞 IO 模型,第二种方式就是非阻塞 IO 模型,第三种方式就是 IO 复用模型了。除了老王,老张老李…都找了黄牛买票,这样大家都可以不用跑火车站了,等黄牛消息就行。黄牛帮一个人买是买,帮多个人买也是买,反正都要在这里排队,还能多挣几份钱。老王老张老李的请求,都复用这个黄牛搞定了,老王他们节省了时间和精力干了其他事,黄牛一个人花费了近乎一样的时间和精力赚了多份钱。

3. 单线程模型降低额外开销

避免多线程切换的性能损耗;
避免访问共享资源加锁的性能损耗;

日常写程序时,我们经常会听到一种说法:“使用多线程,可以增加系统吞吐率,或是可以增加系统扩展性。”的确,对于一个多线程的系统来说,在有合理的资源分配的情况下,可以增加系统中处理请求操作的资源实体,进而提升系统能够同时处理的请求数,即吞吐率。下面的左图是我们采用多线程时所期待的结果。
在这里插入图片描述
通常情况下,在我们采用多线程后,如果没有良好的系统设计,实际得到的结果,其实是右图所展示的那样。我们刚开始增加线程数时,系统吞吐率会增加,但是,再进一步增加线程时,系统吞吐率就增长迟缓了,有时甚至还会出现下降的情况。

为什么会出现这种情况呢?一个关键的瓶颈在于,系统中通常会存在被多线程同时访问的共享资源,比如一个共享的数据结构。当有多个线程要修改这个共享资源时,为了保证共享资源的正确性,就需要有额外的机制进行保证,而这个额外的机制,就会带来额外的开销。

并发访问控制一直是多线程开发中的一个难点问题,如果没有精细的设计,比如说,只是简单地采用一个粗粒度互斥锁,就会出现不理想的结果:即使增加了线程,大部分线程也在等待获取访问共享资源的互斥锁,并行变串行,系统吞吐率并没有随着线程的增加而增加。而且,采用多线程开发一般会引入同步原语来保护共享资源的并发访问,这也会降低系统代码的易调试性和可维护性。为了避免这些问题,Redis 直接采用了单线程模式。

4. 高效合理的数据结构

Redis 中有多种数据类型,每种数据类型的底层都由一种或多种数据结构来支持,比如跳表、HashMap、压缩列表等。正是因为有了这些不同的数据结构,使得数据存储时间复杂度降到最低,Redis 在存储与读取上的速度才不受阻碍。
在这里插入图片描述

总结

Redis使用单线程模型,没有了线程上下文切换和访问共享资源加锁的性能损耗,配合IO多路复用技术,可以完成多个连接的请求处理。而且正是由于它的使用定位是内存数据库,这样几乎所有的操作都在内存中完成,它的性能可以达到非常之高。

5. Redis 6.0 版本为什么又引入了多线程

Redis 的性能瓶颈不在 CPU ,而在内存和网络IO,内存不够可以增加内存或通过数据结构等进行优化;但 Redis 的网络 IO 的读写占用了大部分 CPU 的时间,如果可以把网络处理改成多线程的方式,性能会有很大提升。所以总结下 Redis 6.0 版本引入多线程有两个原因:1. 充分利用服务器的多核资源 2. 多线程分摊 Redis 同步 IO 读写负荷
注意:执行命令还是由单线程顺序执行,只是处理网络数据读写采用了多线程,而且 IO 线程要么同时读 Socket ,要么同时写 Socket ,不会同时读写。

来源:https://zhuanlan.zhihu.com/p/564085399
IO复用例子:https://www.bilibili.com/read/cv18330581

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值