Redis系列(十六)、Redis6新特性之IO多线程

2020/12/25 13:35
阅读数 192

 
  1. readlen;
  2.  
    size_t qblen;
  3.  
    /* Check if we want to read from the client later when exiting from the event loop. This is the case if threaded I/O is enabled. */
  4.  
    // 加入多线程模型已经启用
  5.  
    if (postponeClientRead(c)) return;
  6.  
    // 如果没有启用多线程模型,则走下面继续处理读逻辑
  7.  
    // ....还有后续老逻辑
  8.  
    }

 函数 postponeClientRead() 将任务放入处理队列,而根据上面 IOThreadMain() 和 handleClientsWithPendingReadsUsingThreads() 的任务处理逻辑进行处理


 
  1.  
    /* networking.c: line 2852 */
  2.  
    int postponeClientRead(client *c) {
  3.  
    // 如果启用多线程模型,并且判断全局配置中是否支持多线程读
  4.  
    if (io_threads_active &&
  5.  
    server.io_threads_do_reads &&
  6.  
    // 这里有个点需要注意,如果是 master-slave 同步也有可能被认为是普通 读任务,所以需要标识
  7.  
    !(c->flags & (CLIENT_MASTER|CLIENT_SLAVE|CLIENT_PENDING_READ)))
  8.  
    {
  9.  
    c->flags |= CLIENT_PENDING_READ;
  10.  
    // 将任务放入处理队列
  11.  
    listAddNodeHead(server.clients_pending_read,c);
  12.  
    return 1;
  13.  
    } else {
  14.  
    return 0;
  15.  
    }
  16.  
    }

对比Memcached 

前些年memcached 是各大互联网公司常用的缓存方案,因此redis 和 memcached 的区别基本成了面试官缓存方面必问的面试题,最近几年memcached用的少了,基本都是 redis。不过随着Redis6.0加入了多线程特性,类似的问题可能还会出现,接下来我们只针对多线程模型来简单比较一下它们。

首先看一下Memcached的线程模型:

如上图所示:Memcached 服务器采用 master-woker 模式进行工作,服务端采用 socket 与客户端通讯。主线程、工作线程 采用 pipe管道进行通讯。主线程采用 libevent 监听 listen、accept 的读事件,事件响应后将连接信息的数据结构封装起来,根据算法选择合适的工作线程,将连接任务携带连接信息分发出去,相应的线程利用连接描述符建立与客户端的socket连接 并进行后续的存取数据操作。

Redis6.0与Memcached多线程模型对比
相同点:都采用了 master线程-worker 线程的模型
不同点:Memcached 执行主逻辑也是在 worker 线程里,模型更加简单,实现了真正的线程隔离,符合我们对线程隔离的常规理解。而 Redis 把处理逻辑交还给 master 线程,虽然一定程度上增加了模型复杂度,但也解决了线程并发安全等问题。

尾巴

大家都会拿Redis和memcached对比,但Redis不是memcached,它只是做到like memcached的多线程,而不是跟memcached一样的完全隔离的多线程模型。Redis中因为有lua脚本,事务,Lpush等等复杂性,需要考虑的问题很多,不管怎么样,最新版的Redis6带给我们的IO多线程着实是个惊喜,互联网大厂们应该很快就会纷纷上线此功能了!

 

参考

https://ruby-china.org/topics/38957

http://www.web-lovers.com/redis-source-6-rc-mult-thread.html

https://zhuanlan.zhihu.com/p/76788470

http://calixwu.com/2014/11/memcached-yuanmafenxi-xianchengmoxing.html

希望本文对你有帮助,请点个赞鼓励一下作者吧~ 谢谢!

 

 

 

 

以下来源于  https://www.v2ex.com/t/646669

综述

本次新增的代码位于networking.c中,很显然多线程生效的位置就能猜出来是在网络请求上。作者希望改进读写缓冲区的性能,而不是命令执行的性能主要原因是:

  • 读写缓冲区的在命令执行的生命周期中是占了比较大的比重
  • Redis 更倾向于保持简单的设计,如果在命令执行部分改用多线程会不得不处理各种问题,例如并发写入、加锁等

那么将读写缓冲区改为多线程后整个模型大致如下: 

 

以下来源于  http://xiaorui.cc/archives/6918

不太推荐使用redis6多线程?

就拿官方推荐的4个io thread线程来说,4个io线程加主线程都cpu 100% 的情况下才可超过接近一倍的qps,那还真不如使用redis cluster集群方案 😅。当然redis cluster是有运维成本,对于一些组合的多指令需要智能客户端或代理层解决。

在社区中跟阿里云redis团队聊过,他们的redis多线程为流水线模型,减少了过多的轮询开销。设计上有些像memcached,类似multi reactor的网络模型设计,主线程去监听新连接,通过pipe来通知其他线程新连接,其他线程各自构建event loop。😅

下面是在社区里找到的关于阿里云redis流水线模型设计,单看设计模型确实要比redis6显得优雅些,也更好理解。但由于阿里云redis非开源版,所以性能消耗如何不得而知了。

aliyun 多线程 redis

主线程接受连接,创建client,将连接转发给IO线程。IO线程处理连接的读写事件,解析命令,将解析的完整命令转发给WORKER线程处理,发送response包,负责删除连接等。WORKER线程负责命令的处理,生成客户端回包,定时器事件的执行等。

主线程,IO线程,WORKER线程都有单独的事件驱动,线程之间通过无锁队列交换数据,通过管道进行消息通知。这样的设计可以让不同工种的线程都可以并行跑起来,而redis6同一时间只能跑一块逻辑,要么正监听获取ae事件, 要么几个io thread在解封包,要么在执行数据处理逻辑,而阿里云的redis看设计是可以同时工作起来。

据阿里云redis团队说,在常规使用的需求下,他们的提升最少有三倍左右。近几年阿里云对redis的代码贡献已经排在第三了,仅次于作者和redislabs。阿里云真是没少折腾呀。😁

总结

redis6的io多线程到底实不实用? 看你的需求了,个人建议上redis集群来扩展高并发的需求。

 

 

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部