追求性能极致:Redis6.0的多线程模型( 二 )

  • Redis 通过 AE 事件模型以及 IO 多路复用等技术,拥有超高的处理性能,因此没有使用多线程的必要 。
  • 可以看出,Redis对CPU计算力的要求并不迫切,相反单线程机制让 Redis 内部实现的复杂度大大降低,同时降低了因为上下文切换和资源竞争造成的性能损耗 。那既然单线程这么好用,为什么要引入多线程模式 。
    6.0之后的多线程主要解决什么问题我们知道,近年来底层网络硬件性能越来越好,Redis 的性能瓶颈逐渐体现在网络 I/O 的读写上,单个线程处理网络 I/O 读写的速度跟不上底层网络硬件执行的速度 。从下图我们可以看到,Redis 在处理网络数据时,调用 epoll 的过程是阻塞的,这个过程会阻塞线程 。如果并发量很高,达到万级别的 QPS,就会形成瓶颈,影响整体吞吐能力 。
    追求性能极致:Redis6.0的多线程模型

    文章插图
    既然读写网络的 read/write 系统调用占用了Redis 执行期间大部分CPU 时间,那么要想真正做到提速,必须改善网络IO性能 。我们可以从这两个方面来优化:
    • 提高网络 IO 性能,典型实现方式比如使用 DPDK 来替代内核网络栈的方式
    • 使用多线程,这样可以充分利用多核CPU,同类实现案例比如 Memcached 。
    协议栈优化的这种方式跟 Redis 关系不大,所以最便捷高效的方式就是支持多线程 。总结起来,redis支持多线程就是以下两个原因:
    • 可以充分利用服务器CPU的多核资源,而主线程明显只能利用一个
    • 多线程任务可以分摊 Redis 同步 IO 读写负荷,降低耗时
    6.0版本优化之后,主线程和多线程网络IO的执行流程如下:
    【追求性能极致:Redis6.0的多线程模型】
    追求性能极致:Redis6.0的多线程模型

    文章插图
    具体步骤如下:
    • 主线程建立连接,并接受数据,并将获取的 socket 数据放入等待队列;
    • 通过轮询的方式将 socket读取出来并分配给 IO 线程;
    • 之后主线程保持阻塞,一直等到 IO 线程完成 socket 读取和解析;
    • I/O 线程读取和解析完成之后,返回给主线程 ,主线程开始执行 Redis 命令;
    • 执行完Redis命令后,主线程阻塞,直到IO 线程完成 结果回写到socket 的工作;
    • 主线程清空已完成的队列,等待客户端新的请求 。
    本质上是将主线程 IO 读写的这个操作 独立出来,单独交给一个I/O线程组处理 。这样多个 socket 读写可以并行执行,整体效率也就提高了 。同时注意 Redis 命令还是主线程串行执行 。
    开启多线程的方式Redis6.0的多线程默认是禁用的,只使用主线程 。如需开启需要修改redis.conf配置文件:
    # io-threads-do-reads noio-threads-do-reads yes开启多线程后,还需要设置线程数,否则是不生效的 。同样修改redis.conf配置文件 。关于线程数的设置,官方有一个建议:4 核的机器建议设置为 2 或 3 个线程,8核的建议设置为 6 个线程,线程数一定要小于机器核数 。线程数并不是越大越好,官方认为超过了 8 个就很难继续提效了,没什么意义 。
    # 假设你的CPU核数是8核,尽量配置成 5~6io-threads 5总结