本文转载自微信公众号《后端技术罗盘》,作者大白。转载本文请联系后台技术指南针公众号。1.老套的面试题我们一定经常听到一个烂大街的面试题:为什么Redis选择单线程?这个问题其实并不严谨。为什么这么说:Redis有很多版本3.x,4.x,对于6.x,不同的版本有不同的架构。不限版问这种问题是不是有点耍流氓?限制版本之后,比如4.x,严格来说Redis不是单线程的,而是负责处理客户端请求的线程是单线程的。最新的6.0版本告别了大家印象中的单线程,采用了全新的多线程来解决问题。如果这样回答,面试官大概会想:啊,遇到高手了,反正我对这个问题不是很了解,听听他是怎么解决的吧!Redis版本迭代和里程碑Redis从发布到现在已经走过了十年。一年多了,一直按照自己的命名规则:如果版本号第二位是奇数,就是不稳定的版本比如2.7、2.9、3.1如果版本号第二位是偶数,是2.6,2.8,3.0,3.2等稳定版本当前的奇数版本是下一个稳定版本的开发版本。例如,2.9版本是3.0版本的开发版本。我们在生产环境中一般会选择稳定版进行部署,每个大版本之间会有好几个版本。小版本,最新版本是6.2.2。我们可以通过redis.io官网下载我们感兴趣的版本进行源码阅读:历史发布版本源码:https://download.redis.io/releases/有几个里程碑版本,我们需要理解:5.??0版是直接升级到6.0版。对于这次激进的升级,antirez非常有信心和兴奋,于是他马上发表了一篇文章来讲解6.0的一些主要功能《Redis6.0.0GAisout!》:http://antirez.com/news/132注:GA是GeneralAvailable的缩写,意思是开发团队认为这个版本是一个稳定版本。说了这么多,就是想让大家清楚Redis是与时俱进的。不要以为Redis一直都是单线程的。2、Redis之父不同。我们常说文字如人,对于我们程序员来说,代码如人,也很贴切。从多个历史版本中,我们可以隐约感受到Redis之父Antirez是一个非常特别的人。我明白你说的不是废话。毕竟你是顶尖的程序员,怎么可能轻易跟风。还是举个实际的例子来感受一下,总有什么办法可以做到不一样。集群解决方案Redis单机版出来之后,官方集群版已经很久没有发布了。这个时候业界已经发布了一些集群方案,比较有代表性的有codis和Tweproxy,都是集中式方案和中台方案。想法。由于市场需求旺盛,这两个解决方案很快被多家公司应用到生产环境中。但是,官方的集群方案已经很久没有发布了。这一等就是4年,直到2015年4月1日才发布。同样,Antirez也兴奋地发了一篇文章《Redis集群,不再是汽件》。http://antirez.com/news/79备注:标题可以翻译成Redis集群不再是天方夜谭。在官方的集群方案中,采用了P2P模型的去中心化思想,通过槽的方式实现一致性哈希,通过gossip协议实现集群通信,整体结构更加简洁。三、Redis6.0多线程的奥秘Redis作为基于内存的NoSQL,可以说是高性能的代名词,上万QPS在生产环境中司空见惯。试想一下,Redis如何进一步提升性能呢?这大概是Redis之父一直苦思冥想的问题。先擒贼先擒王。如果你想提高性能,你需要看看卡住了什么。Redis的瓶颈是什么一般来说,多线程对提高CPU利用率有重要作用,但是Redis对提高CPU利用率不感兴趣。在Redis看来,如果要提高CPU利用率,那么在一台机器上部署多个实例就够了。其实想想Redis之所以如此偏爱单线程,肯定是尝到了单线程的甜头:业务模型简单,并发读写没问题,单线程是完全无锁的,无死锁,线程切换丢失,性能非常好。数据结构是有线程安全保证的,大家放心。。。其实Redis4.0引入了多线程实现异步删除数据等功能,但是处理读写请求的还是只有一个线程,所以它仍然是狭义的单线程。抛开CPU,影响Redis性能的主要区域就剩下了:内存和网络IO。内存更多的是属于硬件范畴的东西。例如,我们使用容量更大、吞吐量更高的存储介质进行优化。所以Redis可以优化的空间是有限的。最后可以初步确定Redis的瓶颈为:网络IO。Redis的基本架构在优化网络IO之前,我们需要回顾一下Redis整体的单线程架构:Redis采用的是Reactor模式的网络模型。对于一个客户端请求,主线程负责一个完整的处理过程:从socket读取数据向socket写入数据是一个比较耗时的网络IO操作,解析请求和内存交互的耗时可能会少很多比IO操作。对于这种问题,我们常见的解决方案是标准的多线程:这个方案中工作线程的作用是一样的,MemCached就采用了这个方案。具体过程:Memcached采用master-woker模式工作,主线程Libevent用于监听和处理事件。主线程将连接请求封装为一个任务放入队列中,并根据算法选择一个空闲的工作线程。相应的worker线程处理完后,通过soeket与client进行交互。但是Redis6.0的多线程并没有做到这一点。Redis自身的多线程单线程对Redis的好处可能更大。还有一点就是,如果做成标准化的多线程,Redis可能会比较难处理,因为多线程带来的线程安全问题,以及底层复杂的数据结构操作,难度都很大。Redis6.0将处理过程中最耗时的Socket读取、请求解析、写入外包出去,其余命令执行仍然由单线程完成,与内存中的数据进行交互。这样一来,网络IO操作变成了多线程,而其他核心部分仍然是线程安全的,确实是一个很好的妥协。画外音:Redis6.0通过多IO线程读写网络数据,解析请求协议。对于真正的命令执行,还是使用主线程来操作。真的是很特别的多线程!4.总结这篇文章是最作为介绍性的文章,我们将在下一期讨论Redis多线程的更多细节。