众所周知 redis 6.0 两个比较大的特性 一个是多线程IO 一个是ACL。
今天主要讲解下 IO 多线程特性,以及我的一些看法。结尾有对阿里云redis的一些调研彩蛋。哈哈
多线程的读IO逻辑
acceptCommonHandler readQueryFromClient postponeClientRead handleClientsWithPendingReadsUsingThreads /* 大概的io处理逻辑都在 postponeClientRead函数中汇总 所有io读事件,然后在 时间事件stopThreadedIOIfNeeded和beforeSleep函数中进行读事件的分发,然后处理命令。 那么看下处理逻辑函数handleClientsWithPendingReadsUsingThreads 将所有 postponeClientRead函数中新增的 client 队列分发给 每个io线程对应的io_threads_list[io_nums] 这里主线程 mainthread 是当做 第一个IO线程来处理io的。通过io_threads_op来标志这次io线程是 io_threads_op读操作 还是 IO_THREADS_OP_WRITE 写操作。因为只有一个线程来操作这个 io_threads_op遍历,那么MESI可见性来说其他CPU都是可见最新的M变量的。 setIOPendingCount(j, count); 设置变量处理标志。 然后主线程处理完成自己的IO后 开始傻傻等待所有io处理完成。难道就不能做些其他的操作么,这点需要优化下。。比如:主从同步等操作。cpu空转总归不友好。 */ while(1) { unsigned long pending = 0; for (int j = 1; j < server.io_threads_num; j++) pending += getIOPendingCount(j); if (pending == 0) break; } /* 但是有一点就是 IO线程真的就是傻傻的看变量 部分IOThreadMain 代码 */ /* Wait for start */ for (int j = 0; j < 1000000; j++) { if (getIOPendingCount(id) != 0) break; } /* Give the main thread a chance to stop this thread. */ if (getIOPendingCount(id) == 0) { pthread_mutex_lock(&io_threads_mutex[id]); pthread_mutex_unlock(&io_threads_mutex[id]); continue; } //最后设置自己完成io的标志 setIOPendingCount(0)
写IO是如何调用的
//写的逻辑几乎和读一直这里就简单说下在哪里调用执行 beforeSleep handleClientsWithPendingWritesUsingThreads /* 总体逻辑和读一样 所有io分发io任务,主线程等待完成,完成后又剩余的主从IO事件 */
总体逻辑
引用一个图,懒得画了,大概就是这样
彩蛋来了
io_threaded_reads_processed:0 io_threaded_writes_processed:0
经过对redis主从高可用的实例 压测,可以看到 这个值标志这 社区版的6.0 也是没有开启io线程的。因为对于io线程来说 太影响cpu使用和 抢占了对云环境的应用不是很友好,因为亲和性这些东西也不是很确定的特性。只有在资源分配的时候 剩余多一点cpu资源减少抢占,从而满足客户的 低延迟等问题。
个人意见就是 虚拟环境下 所有的IO线程和 网卡多队列都放到某个socket的 某些cpu的亲缘性高一点,主线程尽量独立cpu亲缘性,从而让 主线程的抢占概率降低。