首页 > 数据库 > 国内数据库 > 阿里云Redis多线程性能提升思路解析
众所周知 redis 是单进程单线程模型(不完全是单进程单线程,还有若干后端线程主要做刷脏数据,关闭文件描述符等后台清理工作)。 redis 中负责主要工作的是主线程,主线程的工作包括但不限:接收客户端连接,处理连接读写事件,解析请求,处理命令,处理定时器事件,数据同步等相关工作。单进程单线程只能跑满一个 CPU 核,在小包场景下,单个 redis server 的 QPS 在8~10万级别。如果 QPS 超过这个级别,单个 redis server 就无法满足需求。而常用的解决办法就是数据分片,采用多 server 的分布式架构予以解决。然而数据分片,多 redis server 方式也存在若干问题: redis server 过多,难以管理;分片之后一些在单 redis server 上使用的命令无法支持;分片无法解决热点读写问题;分片后数据倾斜,数据重分布,数据扩缩容等也比较复杂。由于单进程单线程的局限,我们期望通过多线程的改造以期充分利用SMP多核架构的优势,从而达到提高单 redis server 吞吐的目的。对 redis 做多线程化,最容易想到的方案是每个线程既做 IO 又做命令处理等工作,但由于 redis 处理的数据结构相对比较复杂,多线程需要锁来保证线程安全性,而锁粒度处理不好性能反而可能会出现下降。
我们的思路是通过增加 IO 线程,将连接中数据的读写,命令的解析和数据包的回复放到单独的 IO 线程来处理,而对命令的处理,定时器事件的执行等仍让单一的线程来处理,以此达到提高单 redis server 吞吐的目的。
因为单进程单线程模型的限制, redis 在实现上将耗时的操作分解成多步,多次来执行(例如dict rehash, 过期key删除等操作),尽量避免长时间执行一个操作,从而避免长时间阻塞在一个操作上。单进程单线程代码编写简单,可以减少多进程多线程导致的上下文切换和锁的争抢。
只能使用一个CPU核,无法发挥多核优势。
对于重IO应用来说,大量的cpu耗费在网络IO操作上。对于将 redis 做为缓存的应用,往往都是重IO的应用。这类应用基本上都是QPS很高,使用的命令相对比较简单(多为get,set,incr等操作),但是对RT响应很敏感。这类应用通常带宽占用很高,甚至会跑到百兆级别。当前由于万兆,25G网卡的普及,网络往往已不再是瓶颈,而如何发挥多核优势,充分发挥网卡性能成为需要考虑的事情。
主线程( MAIN THREAD )
IO线程( IO THREAD )
WORKER线程( WORKER THREAD )
主线程:接受连接,创建 client ,将连接转发给IO线程。
IO线程:处理连接的读写事件,解析命令,将解析的完整命令转发给 WORKER 线程处理,发送 response 包,负责删除连接等。
WORKER线程:负责命令的处理,生成客户端回包,定时器事件的执行等。
主线程, IO 线程, WORKER 线程都有单独的事件驱动。
线程之间通过无锁队列交换数据,通过管道进行消息通知。
从压测结果来看,小包场景下,读写性能差不多有三倍左右的性能提升。
Master 向 Slave 发送同步数据时,数据在 IO 线程中发送, Slave 从主读取数据时,全量数据在 WORKER 线程中读取,增量数据在 IO 线程中读取,因此可以相对比较有效的增加同步的速度。
现在所做的第一部分工作是增加IO线程,优化 IO 读写能力。进一步的优化可以考虑对 WORKER 线程进行拆分:每个线程既负责 IO 读取,也负责 WORKER 工作处理。
从测试结果来看, IO 线程数最大不要超过6个。超过之后对简单操作来说, WORKER 线程往往已经成为瓶颈。
进程在启动时需要设置 IO 线程的个数,在进程运行期间 IO 线程个数无法修改,按当前的连接分配策略,修改 IO 线程的个数涉及到连接的重新分配,处理相对比较复杂。
随着万兆网卡,25G网卡的普及,如何充分利用硬件的性能需要充分的考虑。多网络 IO 线程, By pass 内核的用户态协议栈等都是可利用的技术。
通过 IO 线程实现数据的迁移,可以无阻塞, IO 线程对数据进程 Encode ,或者命令转发,目标节点实现数据 Decode ,或者命令执行。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/31550522/viewspace-2213286/ ,如需转载,请注明出处,否则将追究法律责任。