• Redis6的IO多线程分析


    性能测试

    机器配置

    C++
    Architecture:          x86_64
    CPU op-mode(s):        32-bit, 64-bit
    Byte Order:            Little Endian
    CPU(s):                14
    On-line CPU(s) list:   0-13
    Mem:            62G
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    性能

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    配置推荐

    官方表示,当使用redis时有性能瓶颈时,才推荐开启该功能,但是消耗更多的cpu time。并且,开启i/o多线程,至少要是4核以上的cpu,并且需要预留一个空闲cpu。比如4核就配io-threads=2,8核至多配io-threads=6。对于4核以上的机器,官方更推荐配置io-threads=4,因为再往上叠加,收益相对较低了,也没有太大必要。
    另外,io-threads-do-reads配置默认no。i/o多线程默认是的是写socket多线程,socket连接本身就是epoll多路复用模式,理解下来开启此配置对性能不太有提升空间。

    分析

    多线程模型

    并非是标准的Reactor多线程模型。主线程将就绪的socket交给IO线程去读写,IO线程处理完后主线程开始进行下一步操作。
    在这里插入图片描述

    流程图

    在这里插入图片描述

    IO线程执行函数代码分析

    void *IOThreadMain(void *myid) {
        long id = (unsigned long)myid;
        char thdname[16];
    
        snprintf(thdname, sizeof(thdname), "io_thd_%ld", id);
        redis_set_thread_title(thdname);
        
        //设置线程的CPU亲和性
        redisSetCpuAffinity(server.server_cpulist); 
        
        //设置线程可以在任意时刻被kill
        //pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
        //pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
        makeThreadKillable();   
    
        while(1) {
            /* Wait for start */
            //主线程setIOPendingCount(id, count)时,这里先空转一下。
            for (int j = 0; j < 1000000; j++) {
                if (getIOPendingCount(id) != 0) break;
            }
    
            /* 如果pending count 小于线程数*2,或者io_threads_num == 1,那么交给主线程自己处理,阻塞子线程。
            *   (入口是:handleClientsWithPendingWritesUsingThreads->stopThreadedIOIfNeeded)
            *  主线程尝试加锁,加锁成功后,子线程就阻塞在这里了。(见:stopThreadedIO)
            */
            if (getIOPendingCount(id) == 0) {
                pthread_mutex_lock(&io_threads_mutex[id]);
                pthread_mutex_unlock(&io_threads_mutex[id]);
                continue;
            }
    
            serverAssert(getIOPendingCount(id) != 0);
    
            /* Process: note that the main thread will never touch our list
             * before we drop the pending count to 0. */
            listIter li;
            listNode *ln;
            //每个线程都有自己的list,遍历list执行就序操作。
            listRewind(io_threads_list[id],&li);    
            while((ln = listNext(&li))) {
                client *c = listNodeValue(ln);
                //handleClientsWithPendingWritesUsingThreads()会把就序操作设置为IO_THREADS_OP_WRITE
                if (io_threads_op == IO_THREADS_OP_WRITE) {
                    writeToClient(c,0);    //写
                //handleClientsWithPendingReadsUsingThreads()会把就序操作设置为IO_THREADS_OP_READ
                } else if (io_threads_op == IO_THREADS_OP_READ) {
                    readQueryFromClient(c->conn);    //读(需开启io-threads-do-reads)
                } else {
                    serverPanic("io_threads_op value is unknown");
                }
            }
            listEmpty(io_threads_list[id]);
            // 置0,表示这个线程已经处理完了。
            setIOPendingCount(id, 0);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
  • 相关阅读:
    开发上门按摩系统对技师如何管理,薪资结构怎么设计
    QThread Class
    【算法题】素数伴侣-用匈牙利算法解决二分图匹配问题
    Windows和Mac命令窗快速打开文件夹
    【设计模式】四、工厂模式
    SAP 10策略测试及简介
    JNDI注入分析
    ASP.NET Core 6框架揭秘实例演示[21]:如何承载你的后台服务
    原型模型(clone()和拷贝构造器之间的选择)
    Twisted 与 Tornado 中的 WebSocket 连接问题及解决方案
  • 原文地址:https://blog.csdn.net/I_can_/article/details/134338377