我是Redis,今年11岁~曾几何时,我是那么的单纯,那么的可爱,现在却背叛了当初的“誓言”,立志要在多元的道路上一路狂奔-毫不犹豫的穿线,没错我就是你们口中可爱迷人的Redis,你们可以叫我小R...R😊。秀场运营的浪潮结束了,下面开始今天的正文。我们知道在Redis4.0之后,陆续加入了一些多线程的功能。单线程不好吗?单线程慢吗?曾几何时,Redis的单线程是我们炫耀的资本。其优雅高效的设计让无数追求者着迷。你想问我排名第几?Nginx是我大哥,NodeJS是我弟弟,我是老二。我们三兄弟可谓是单线程的杰出代表,既彰显了我们的优雅,也彰显了我们的高效。可能有人会问:为什么我是单线程的就这么嚣张?家里有矿。Redis是单线程但仍然很快的主要原因有以下几点:1.基于内存的操作:Redis中的所有数据都存储在内存中,所以所有的操作都在内存层面,所以它的性能比较高;2、简单的数据结构:Redis的数据结构比较简单,是专门为Redis设计的,这些简单数据结构的查找和运算的时间复杂度是O(1),所以性能比较高;3.多路复用和非阻塞I/O:Redis利用I/O多路复用功能,监听多个socket连接客户端,这样一个线程连接可以处理多个请求,减少线程切换4.避免上下文切换:因为它是单线程模型,避免了不必要的上下文切换和多线程竞争,节省了消除了多线程切换带来的时间和性能消耗,单线程也不会出现死锁问题。来看看下面我的父亲大是如何评价我的,Redis的FAQ(常见问题,常见问题)回答了单线程序的这个问题,具体内容如下:Redis是单线程的。我如何利用多个CPU/内核?CPU成为Redis瓶颈的情况并不常见,因为Redis通常受内存或网络限制。例如,使用在普通Linux系统上运行的流水线Redis每秒甚至可以传递100万个请求,因此如果您的应用程序主要使用O(N)或O(log(N))命令,则几乎不会占用太多CPU.然而,为了最大限度地提高CPU使用率,您可以在同一个盒子中启动多个Redis实例,并将它们视为不同的服务器。在某些时候,单个盒子可能还不够,所以如果你想使用多个CPU,你可以开始考虑一些更早分片的方法。你可以在分区页面中找到有关使用多个Redis实例的更多信息。但是对于Redis4.0我们开始让Redis更加线程化。目前这仅限于删除背景,并阻止通过Redis模块实现的命令。对于未来的版本,计划是让Redis越来越线程化。有关详细信息,请参阅:https://redis.io/topics/faq。他一般的意思是Redis是基于内存运行的,所以它的瓶颈可能是机器的内存或者网络带宽,而不是CPU。既然CPU不是瓶颈,自然就采用了单线程的方案。再说使用多线程也比较麻烦,不过Redis4.0开始支持多线程,比如后台删除等功能。简单来说,Redis在4.0之前一直使用单线程的原因主要有以下三个:使用单线程模型使得Redis的开发和维护更加容易,因为单线程模型方便开发和调试;即使使用单线程模型,并发处理多客户端请求主要使用多路复用和非阻塞IO;对于Redis系统来说,主要的性能瓶颈是内存或者网络带宽,而不是CPU。为什么需要多线程?但是单线程也有单线程的麻烦。比如我(Redis)需要删除大量数据的时候,因为是单线程同步操作,这会导致Redis服务卡死,所以在Redis4.0中,增加了很多新的功能。thread模块,当然这个版本的多线程主要是为了解决数据删除效率低的问题,其相关指令包括以下三个:unlinkkeyflushdbasyncflushallasync执行示例如下:>unlinkkey#后台删除某键>OK#执行成功>flushallasync#清除所有数据>OK#执行成功这样我就可以“瞬间”屏蔽(删除)这些坏人了。所谓“瞬间”删除其实有点夸张,但是从返回结果来看,删除成功了,不过这只是把删除工作交给了后台的小弟(子线程)异步删除数据.Tips:一般情况下,del命令可以用来快速删除数据,而当要删除的key是一个非常大的对象时,比如包含几千个元素的hash集合,那么del命令会导致mainRedis的线程会卡死,所以使用延迟删除可以有效避免Redis卡死的问题。在Redis6多线程之前,在Redis4.0,你说删除比较慢,骗我开辟(多线程)来处理就好了。为什么Redis6.0需要多线程?实际上,虽然Redis4.0版本引入了多线程,但是这个版本的多线程只能用于大数据量的异步删除,对于非删除操作意义不大。但是如果我们在非删除环境下使用多线程,就可以分担Redis同步读写I/O的压力,充分利用多核CPU的资源,从而有效提高QPSRedis(QueryPerSecond,每秒查询率)。Redis中虽然使用了I/O??多路复用,并且基于非阻塞I/O进行操作,但是I/O本身的读写是阻塞的。例如,当socket中有数据时,Redis会通过调用先将数据从内核态空间复制到用户态空间,再传递给Redis进行调用,复制过程被阻塞。当数据量较大时,复制耗时较长,这些操作都是基于单线程完成的。I/O多路复用,简单来说就是一种机制,通过监听文件的读写事件,然后通知线程执行相关操作,来保证Redis的非阻塞I/O能够顺利执行。因此,在Redis6.0中,新增了多线程功能,以提高I/O的读写性能。他的主要实现思路是将主线程的I/O读写任务拆分成一组独立的线程执行,这样可以并行的实现多个socket的读写,但是Redis的命令还是串行执行的通过主线程。需要注意的是,Redis6.0默认是关闭多线程的。可以通过修改Redis配置文件redis.conf中的io-threads-do-reads等于true来开启多线程。完整的配置是io-threads-do-readstrue,另外我们还需要设置线程数才能正确开启多线程功能,另外还要修改Redis的配置,比如设置io-threads4意味着启用4个线程。Tips:关于线程数的设置,官方的建议是,如果是4核CPU,建议将线程数设置为2或3,如果是8核CPU,则建议线程数设置为6,线程数一定要小于机器核数。线程数并不是越大越好。关于Redis的性能,我爸antirez(Redis的作者)在RedisConf2019上提到,Redis6引入的多线程I/O特性至少让性能提升了一倍。国内也有人用4线程Redis版本和单线程Redis在阿里云上进行对比测试,发现测试结果与antirez给出的结论基本吻合,性能基本可以翻倍。小结虽然Redis依靠自身的特点:基于内存操作,数据结构简单,多路复用和非阻塞I/O,避免不必要的线程上下文切换,但在单线程环境下还是很快的;但是对于大数据的key删除还是卡的飞,所以在Redis4.0引入了多线程:unlinkkey/flushallasync等命令,主要用于删除Redis数据,I/O多线程读取Redis6.0引入了写入,可以更高效地处理更多的任务。Redis只是把I/O读写改成了多线程,而命令的执行还是由主线程串行执行。因此,在多线程下操作Redis是没有必要的。会有线程安全问题。无论是Redis最初的单线程设计,还是违背最初设计的多线程设计,目的都只有一个:让Redis越来越快。所以Redis还是没变,他还是那个追风的少年~
