当前位置:首页 > 问答 > 正文

Redis多线程到底是咋回事,性能提升背后那些没说透的秘密

关于Redis多线程这事儿,很多人只知道它从6.0开始变成了“多线程”,性能变强了,但具体强在哪儿,为啥要这么改,里面有不少门道,我今天就掰开揉碎了讲讲,把那些宣传稿里没太说透的秘密给你唠明白。

最核心的一点必须说清楚:Redis的多线程,到现在也不是用来同时执行你的读写命令的! 这句话是关键,很多人一听说多线程,就以为像传统数据库那样,可以多个CPU核心同时处理多个SETGET命令了,不是的,Redis的核心内存数据库操作,也就是真正处理你发来的那些命令的环节,依然是单线程的。

(来源:Redis官方文档及创始人antirez的博文多次强调,核心命令处理是单线程的)

那它多线程了个啥呢?主要是多在了网络I/O一些后台的重体力活儿上。

瓶颈其实在网卡,不在CPU

在以前单线程的年代,Redis那个独苗主线程啥都得干,它不仅要吭哧吭哧地执行你的命令,还得亲自去处理网络上的事儿,就是得一个个地从网络套接字里读取客户端发来的命令数据,然后执行,执行完了再一个个地把结果写回套接字,发还给客户端。

当你的业务QPS(每秒请求数)不高,或者每个命令处理起来很快时,这个单线程模型非常高效,因为完全没有线程切换和多线程竞争锁的开销,但问题出在,当连接数非常多(比如成千上万),或者网络流量特别大时,光是读取请求和写回响应这两个网络I/O操作,就会占据大量的时间,主线程大部分时间可能都在忙活“搬数据”这种纯体力活,而不是在干它最擅长的“计算”(执行命令)本职工作,这就好比一个顶尖的大厨,却要亲自去洗菜、切菜、洗碗,真正炒菜的时间反而被挤占了。

(来源:antirez在介绍Redis 6.0多线程的博文中详细解释了网络I/O成为瓶颈的问题)

Redis多线程到底是咋回事,性能提升背后那些没说透的秘密

Redis 6.0引入的多线程I/O,就是给主厨配了几个切菜洗菜的小工,这些小工(I/O线程)负责帮主线程完成最耗时的部分:把命令数据从网络连接中读出来,解析好,放进一个队列里;以及把主线程处理完的结果,从队列里取出来,写回到网络连接中,这样一来,主线程就能专心致志、一刻不停地从队列里取命令、执行命令、再把结果放回输出队列,效率大大提升。

性能提升有前提,不是万能药

这就是第一个没说透的秘密:多线程I/O的提升效果,严重依赖于你的使用场景,如果你的应用是少量长连接,每个连接发的命令也不密集,那可能完全感受不到提升,甚至因为线程切换还有一点点损耗,但如果你是那种需要维持数万甚至十万级别并发连接,每个连接虽然不活跃但时不时要请求一下的场景(比如物联网),或者网络带宽非常高(比如万兆网卡),那么启用多线程I/O带来的性能提升将是巨大的,官方数据是可以翻倍。

(来源:Redis 6.0发布时的性能测试报告显示了在高并发连接下性能的显著提升)

真正的“重体力活”早已偷偷多线程化了

Redis多线程到底是咋回事,性能提升背后那些没说透的秘密

除了网络I/O,另一个多线程的领域是一些阻塞性的后台操作,这个其实在更早的版本就开始了。

  1. 持久化时的BGREWRITEAOF:生成新的AOF文件是个很耗磁盘I/O的活儿,现在是由后台子进程(或线程)完成的,不阻塞主线程。
  2. 大Key的删除(UNLINK):如果你直接用一个巨大的Key,比如一个包含百万个元素的Hash,执行DEL命令,主线程会卡住很久,因为要一次性释放大量内存,但用UNLINK命令,它只是把Key标记为删除,真正的内存释放工作会交给后台线程慢慢做,主线程立马返回,无感知。
  3. 惰性删除:当某个Key过期后,Redis默认不会立刻删除它,而是等到下次有请求来访问这个Key时,再顺手删除,这个删除动作现在也是放到后台线程池里异步处理的,避免突然清理一大批过期Key时卡住主线程。

(来源:Redis官方文档关于惰性删除、UNLINK命令的说明)

这些操作的特点都是“重”、会阻塞主线程,所以把它们丢到后台去慢慢做,保证主线程对外服务的响应速度,这才是多线程思想在Redis里更早、也更重要的应用。

Redis的多线程,不是一个颠覆性的、把核心模型从单线程改成多线程的壮举,而是一个非常务实和精明的“打补丁”优化,它精准地找到了单线程模型下的两个真正瓶颈——高并发下的网络I/O和某些阻塞性后台任务——然后针对性地用额外的线程去分担这些压力,它的目标始终如一:不惜一切代价,保障那个处理命令的核心主线程能够高速、无阻塞地奔跑。

理解了这一点,你就能明白为什么Redis在引入多线程后依然保持着单线程的简单性优势,又能获得近乎翻倍的吞吐量提升,它不是变成了另一个数据库,它还是那个你熟悉的Redis,只是学会了如何更好地利用现代多核服务器的硬件能力,特别是强大的网络资源,下次再有人跟你聊Redis多线程,你就可以把这些没说透的秘密讲给他听了。