• 一、Redis单线程 vs 多线程


    一、Redis的历史

    Redis的版本很多3.x、4.x、6.x,版本不同架构也是不同的:

    (1)版本3.x ,redis是单线程

    (2)版本4.x,严格意义来说也不是单线程,而是负责处理客户端请求的线程是单线程,但是开始加了点多线程的东西(异步删除)。——貌似

    (3)最新版本的6.0.x后,告别了大家印象中的单线程,用一种全新的多线程来解决问题。

    FAQ:为什么Redis官方的window版本只到3.x?因为unix底层的 I/O 多路复用功能很强大,win不支持epoll函数,无法发挥redis极致效能。

    Redis3.x单线程时代但性能依旧很快的主要原因:

    (1)基于内存操作:Redis 的所有数据都存在内存中,因此所有的运算都是内存级别的,所以他的性能比较高。

    (2)数据结构简单:Redis 的数据结构是专门设计的,而这些简单的数据结构的查找和操作的时间大部分复杂度都是 O(1),因此性能比较高。

    (3)多路复用和非阻塞 I/O:Redis使用 I/O多路复用功能来监听多个 socket连接客户端,这样就可以使用一个线程连接来处理多个请求,减少线程切换带来的开销,同时也避免了 I/O 阻塞操作。

    (4)避免上下文切换:因为是单线程模型,因此就避免了不必要的上下文切换和多线程竞争,这就省去了多线程切换带来的时间和性能上的消耗,而且单线程不会导致死锁问题的发生。

    Redis 4.0 之前一直采用单线程的主要原因:

    (1)使用单线程模型是 Redis 的开发和维护更简单,因为单线程模型方便开发和调试;

    (2)即使使用单线程模型也并发的处理多客户端的请求,主要使用的是多路复用和非阻塞 IO;

    (3)对于 Redis 系统来说,主要的性能瓶颈是内存或者网络带宽而并非 CPU —— Redis之父

    Redis为何发展为多线程?及单线程的弊端:

    正常情况下使用 del 指令可以很快的删除数据,而当被删除的 key 是一个非常大的对象时,那么 del 指令就会造成 Redis 主线程卡顿。这就是redis3.x单线程时代最经典的故障,大key删除的头疼问题

    由于redis是单线程的,del bigKey .....等待很久这个线程才会释放,类似加了一个synchronized锁。

    解决方式有unlink key、flushdb async、flushall async,不推荐使用,实际开发中应该避免产生大key,比如 bigkey-01,bigkey-02.....尽量拆分

    二、Redis6

    1、开启多线程

    Redis将所有数据放在内存中,内存的响应时长大约为100纳秒,对于小数据包,Redis服务器可以处理8W到10W的QPS,这也是Redis处理的极限了,对于80%的公司来说,单线程的Redis已经足够使用了。

    在Redis6.0中,多线程机制默认是关闭的,如果需要使用多线程功能,需要在redis.conf中完成两个设置:

    (1)设置io-thread-do-reads配置项为yes,表示启动多线程。
    (2)设置线程个数。关于线程数的设置,官方的建议是如果为 4 核的 CPU,建议线程数设置为 2 或 3,如果为 8 核 CPU 建议线程数设置为 6,线程数一定要小于机器核数,线程数并不是越大越好。

    2、什么是 I/O 多路复用

    这是IO模型的一种,即经典的Reactor设计模式,I/O 多路复用,简单来说就是通过监测文件的读写事件再通知线程执行相关操作,保证 Redis 的非阻塞 I/O 能够顺利执行完成的机制。

    多路指的是多个socket连接,复用指的是复用一个线程。多路复用主要有三种技术:select,poll,epoll

    epoll是最新的也是目前最好的多路复用技术。采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络IO的时间消耗),且Redis在内存中操作数据的速度非常快(内存内的操作不会成为这里的性能瓶颈),主要以上两点造就了Redis具有很高的吞吐量。

    3、如何实现

    I/O 的读和写本身是堵塞的,比如当 socket 中有数据时,Redis 会通过调用先将数据从内核态空间拷贝到用户态空间,再交给 Redis 调用,而这个拷贝的过程就是阻塞的,当数据量越大时拷贝所需要的时间就越多,而这些操作都是基于单线程完成的。

    在 Redis 6.0 中新增了多线程的功能来提高 I/O 的读写性能,他的主要实现思路是将主线程的 IO 读写任务拆分给一组独立的线程去执行,这样就可以使多个 socket 的读写可以并行化了,采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络IO的时间消耗),将最耗时的Socket的读取、请求解析、写入单独外包出去,剩下的命令执行仍然由主线程串行执行并和内存的数据交互。

    结合上图可知,网络IO操作就变成多线程化了,其他核心部分仍然是线程安全的。


  • 相关阅读:
    Hexo Daemon
    Eclipse远程Debug
    IT人都欠自已一个Lable Page
    Linux下java/bin目录下的命令集合
    python笔记--5--文件操作
    python笔记--4--面向对象
    python笔记--3--函数、生成器、装饰器、函数嵌套定义、函数柯里化
    python笔记--1--基础知识、数据类型
    Django笔记--视图
    C++笔记--1
  • 原文地址:https://www.cnblogs.com/shiblog/p/15801904.html
Copyright © 2020-2023  润新知