Redis 通常被称为单进程、单线程模型。这不是真的。Redis还运行多个后端线程来执行后端清理工作,例如清理脏数据和关闭文件描述符。在Redis中,主线程负责主要任务,包括但不限于:接收客户端的连接、处理连接读/写事件、解析请求、处理命令、处理定时器事件、同步数据。只有一个CPU核心运行一个进程和一个线程。
对于小数据包,Redis 服务器可以处理 80,000 到 100,000 QPS。更大的QPS超出了Redis服务器的处理能力。常见的解决方案是对数据进行分区,采用分布式架构的多台服务器。
然而,该解决方案也有许多缺点。例如,Redis服务器太多而难以管理;一些适用于单个Redis服务器的命令不适用于数据分区;数据分区无法解决热点读写问题;数据倾斜、重新分布和扩展/缩小变得更加复杂。由于单进程、单线程的限制,我们希望能够重构多线程,充分利用SMP多核架构的优势,从而提高单台Redis服务器的吞吐量。
最新的 DZone 参考卡
NoSQL 迁移要点
要使Redis成为多线程,最简单的想法是每个线程都执行I/O和命令处理。但由于Redis处理的数据结构比较复杂,多线程需要使用锁来保证线程安全。锁粒度处理不当可能会降低性能。
我们建议增加I/O线程的数量,让独立的I/O线程能够读写连接中的数据、解析命令、回复数据包,同时仍然让单个线程处理命令并执行定时器事件。这样可以提高单台Redis服务器的吞吐量。
单进程单线程模型
优点
由于单进程、单线程模型的限制,Redis实现中耗时的操作(例如dict rehash、过期key删除等)被分成多个步骤逐一执行。这样可以防止操作长时间执行,从而避免操作长时间阻塞系统。单进程、单线程代码易于编译,减少了多进程、多线程带来的上下文切换和锁占用。
缺点
只能使用一个CPU核心,无法发挥多核优势。
对于大量 I/O 应用程序,大量的 CPU 容量被网络 I/O 操作消耗。使用 Redis 作为缓存的应用程序通常是 I/O 量大的应用程序。这些应用基本上都有较高的QPS,使用相对简单的命令(例如get、set和incr),但对RT敏感。它们通常具有很高的带宽使用率,甚至可能达到数百兆位。随着10GB、25GB网卡的普及,网络带宽不再是瓶颈。因此,我们需要思考的是如何利用多核和网卡性能的优势。
多线程模型及实现
线程模型
线程类型分为三种,分别是:
主线程
输入/输出线程
工作线程
图片标题
主线程:接收连接、创建客户端并将连接转发到 I/O 线程。
I/O线程:处理连接读写事件,解析命令,将解析完成的命令转发给工作线程处理,发送响应包,删除连接。
工作线程:处理命令,生成客户端响应数据包,并执行计时器事件。
主线程、I/O线程和工作线程分别由事件驱动。
线程通过无锁队列交换数据并通过隧道发送通知。
多线程模型的好处
提高读/写性能
压力测试结果表明,小包场景下读写性能可提升3倍左右。
图片标题
提高主/从同步速度
当主机向从机发送同步数据时,数据是在I/O线程中发送的。从主机读取数据时,从工作线程读取全量数据,从I/O线程读取增量数据。这可以有效地提高同步速度。
后续任务
第一个任务是增加I/O线程数,优化I/O读写能力。接下来,我们可以分解工作线程,让每个线程完成I/O读取,以及工作线程的工作。
设置I/O线程数
测试结果表明,I/O线程数不应超过6个。否则,工作线程将成为简单操作的瓶颈。
进程启动时,必须设置 I/O 线程数。进程运行时,I/O 线程数无法修改。根据目前的连接分配策略,I/O线程数的修改涉及到连接的重新分配,比较复杂。
注意事项
随着10GB、25GB网卡的普及,如何充分利用硬件性能是必须认真考虑的问题。我们可以使用网络I/O多线程和内核绕过用户模式协议栈等技术。
利用I/O线程可以实现无阻塞的数据迁移。I/O线程对数据处理进行编码或转发命令,而目标节点对数据进行解码或执行命令。