Redis为什么这么快 经典面试题
Redis有多快
依据官网基准测试,在具备平均配件的Linux机器上运转的单个Redis实例通常可认为便捷命令(O(N)或O(log(N)))成功8w+的QPS,经常使用流水线批处置可以到达100w。
从性能角度来看,Redis可以称为高性能的缓存处置打算。
Redis为什么这么快
面试时经常被问到Redis高性能的要素,典型回答是上方这些:
为什么Redis选用复线程?
上方回答了是复线程的,接着会问为啥驳回复线程模型。
Redis的CPU通常不会成为性能瓶颈,由于通常状况下Redis要么遭到内存限制,要么遭到网络限制。例如,经常使用流水线技术,在平均Linux系统上运转的Redis甚至可以每秒处置100万个恳求,因此,假设运行程序关键经常使用O(N)或O(log(N))命令,它简直不会经常使用太多CPU。
这基本上象征着CPU通常不是数据库的瓶颈,由于大少数恳求不会占用太多CPU资源,而是占用I/O资源。特意是关于Redis来说,假设不思索像RDB/AOF这样的耐久性打算,Redis是齐全的内存操作,十分极速。Redis的真正性能瓶颈是网络I/O,即客户端和主机之间的网络传输提前,因此Redis选用了复线程的I/O多路复用来成功其外围网络模型。
「实践上选用复线程的更详细要素可以总结如下:」
Redis真的是复线程的吗?
在回答这个疑问之前,咱们须要廓清“复线程”概念的范畴:它能否涵盖了外围网络模型或整个Redis?假设是前者,答案是必需的。Redis的网络模型在v6.0之前不时是复线程的;假设是后者,答案是不。Redis早在v4.0版本中就引入了多线程。
复线程网络模型
从Redis v1.0到v6.0,Redis的外围网络模型不时是典型的单Reactor模型:经常使用epoll/select/kqueue等多路复用技术来处置事情(客户端恳求)在复线程事情循环中,最后将照应数据写回客户端。
在这里有几个外围概念须要了解。
Redis外部成功了一个高性能事情库AE,基于epoll/select/kqueue/evport,用于为Linux/MacOS/FreeBSD/Solaris成功高性能事情循环模型。Redis的外围网络模型正式构建在AE之上,包括I/O多路复用和各种处置器绑定的注册,一切这些都是基于它成功的。
到这里,咱们可以形容一个客户端从Redis恳求命令的上班形式。
关于那些宿愿应用多核性能的人来说,官网的Redis处置打算便捷而间接:在同一台机器上运转更多的Redis实例。理想上,为了保障高可用性,一个在线业务不太或许以独立运转的形式存在。更经常出现的是经常使用Redis散布式集群,具备多个节点和数据分片,以提高性能和确保高可用性。
多线程异步义务
如前所述,Redis在v4.0版本中引入了多线程来口头一些异步操作,关键用于十分耗时的命令。经过将这些命令的口头设置为异步,可以防止阻塞复线程事情循环。
咱们知道Redis的DEL命令用于删除一个或多个键的存储值,它是一个阻塞命令。在大少数状况下,要删除的键不会存储太多值,最多几十个或几百个对象,因此可以极速口头。但假设要删除具备数百万个对象的十分大的键值对,则此命令或许会阻塞至少几秒钟,由于事情循环是复线程的,它会阻塞随后的其余事情,从而降落吞吐量。
Redis的作者antirez对处置这个疑问启动了深思熟虑。后来,他提出了一个渐进式的处置打算:经常使用定时器和数据游标,他将逐渐删除大批数据,例如1000个对象,最终肃清一切数据。但这个处置打算存在一个致命的毛病:假设其余客户端继续写入正在逐渐删除的键,而且删除速度跟不上写入的数据,那么内存将无休止地被消耗,这个疑问经过一个奇妙的处置打算得以处置,但这个成功使Redis愈加复杂。多线程似乎是一个牢无法破的处置打算:便捷且容易了解。因此,最终,antirez选用引入多线程来口头这类非阻塞命令。antirez在他的博客中更多地思索了这个疑问:懈怠的Redis是更好的Redis。
因此,在Redis v4.0之后,已参与了一些非阻塞命令,如UNLINK、FLUSHALL ASYNC、FLUSHDB ASYNC等,它们会在后盾线程中口头,不会阻塞主线程事情循环。这使得Redis可以更好地应答一些特定状况下的命令处置。
多线程异步义务的关键特点:
总结 Redis的网络模型是复线程的,这象征着它经常使用单个事情循环来处置一切客户端恳求。这个设计的优势是便捷性和可保养性,但须要审慎处置一些或许造成事情循环阻塞的命令。
为了处置一些十分耗时的命令,Redis v4.0引入了多线程异步义务。这些异步义务在后盾线程中口头,不会阻塞主线程的事情循环,从而提高了Redis的吞吐量和可用性。
总而言之,Redis的复线程事情循环和多线程异步义务的设计是为了在性能和便捷性之间取得平衡,以满足各种不同用例的需求。了解Redis的这些基本原理关于经常使用Redis启动高性能数据存储缓和存十分关键。
多线程网络模型
正如前面提到的,Redis最后选用了复线程的网络模型,要素是CPU通常不是性能瓶颈,瓶颈往往是内存和网络,因此复线程足够了。那么为什么Redis如今引入了多线程呢?便捷的理想是Redis的网络I/O瓶颈变得越来越显著。
随着互联网的极速增长,互联网业务系统处置越来越多的在线流量,而Redis的复线程形式造成系统在网络I/O上消耗了少量CPU时期,从而降落了吞吐量。提高Redis性能有两种形式:
后者依赖于配件的开展,目前尚无法处置。因此,咱们只能从前者入手,网络I/O的优化可以分为两个方向:
零拷贝技术存在局限性,无法齐全顺应像Redis这样的复杂网络I/O场景。DPDK技术经过绕过内核栈来绕过NIC I/O,过于复杂,须要内核甚至配件的允许。
因此,充沛应用多个外围是优化网络I/O最具老本效益的形式。
在6.0版本之后,Redis正式将多线程引入外围网络模型中,也称为I/O线程,如今Redis具备真正的多线程模型。在前面的局部中,咱们了解了Redis 6.0之前的复线程事情循环模型,实践上是一个十分经典的反响器模型。
反响器形式在Linux平台上的大少数干流高性能网络库/框架中都有运行,比如netty、libevent、libuv、POE(Perl)、Twisted(Python)等。
反响器形式实践上是支经常使用I/O多路复用(I/O multiplexing)+非阻塞I/O(non-blocking I/O)形式。
Redis的外围网络模型,直到6.0版本,都是繁多的反响器模型:一切事情都在繁多线程中处置,虽然在4.0版本中引入了多线程,但更多是用于特定场景的补丁(删除超大键值等),不能被视为外围网络模型的多线程。
普通来说,繁多反响器模型,在引入多线程后,调演化为多反响器模型,具备以下基本上班模型。
与繁多线程事情循环不同,这种形式有多个线程(子反响器),每个线程保养一个独立的事情循环,主反响器接纳新衔接并将其散发给子反响器启动独立处置,而子反响器则将照应写回客户端。
多反响器形式通常可以同等于Master-Workers形式,比如Nginx和Memcached经常使用这种多线程模型,虽然名目之间的成功细节略有不同,但总体形式基本分歧。
Redis多线程网络模型设计
Redis也成功了多线程,但不是规范的多反响器/主上班形式。让咱们先看一下Redis多线程网络模型的普通设计。
大局部逻辑与之前的复线程模型相反,惟一的扭转是将读取客户端恳求和写回照应数据的逻辑异步化到I/O线程中。这里须要特意留意的是,I/O线程只担任读取和解析客户端命令,实践的命令口头最终是在主线程上成功的。
总结
当面试官再问Redis为啥这么快时别傻傻再回答Redis是复线程了,否则只能回去等通知了。
Redis的多线程网络模型经过将读取和写回数据的义务异步化,以及更好地利用多核CPU,从而提高了Redis在处置少量在线流量时的性能体现。
1.「多线程设计」:
2.「异步读写」:Redis的多线程模型异步化了读取客户端恳求和写回照应数据的环节。客户端恳求首先被放入待读取队列,而后由I/O线程读取。口头命令依然在主线程上启动,但这种异步化提高了系统的并发性和吞吐量。