关于mongodb:常用高并发网络线程模型设计及MongoDB线程模型优化实践

2次阅读

共计 4077 个字符,预计需要花费 11 分钟才能阅读完成。

服务端通常须要反对高并发业务拜访,如何设计优良的服务端网络 IO 工作线程 / 过程模型对业务的高并发拜访需要起着至关重要的核心作用。

本文总结了了不同场景下的多种网络 IO 线程 / 过程模型,并给出了各种模型的优缺点及其性能优化办法,非常适合服务端开发、中间件开发、数据库开发等开发人员借鉴。

(顺便提一下,OPPO 互联网文档数据库团队急缺 mongodb 内核及存储引擎研发人才,欢送退出 oppo 参加千万级峰值 tps/ 万亿级数据量数据库研发。联系人:yangyazhou#oppo.com)

1. 线程模型一. 单线程网络 IO 复用模型

阐明:

  1. 所有网络 IO 事件 (accept 事件、读事件、写事件) 注册到 epoll 事件集
  2. 主循环中通过 epoll_wait 一次性获取内核态收集到的 epoll 事件信息,而后轮询执行各个事件对应的回调。
  3. 事件注册、epoll_wait 事件获取、事件回调执行全副由一个线程解决

1.1 一个残缺申请组成

一个残缺的申请处理过程次要蕴含以下几个局部:

步骤 1:通过 epoll_wait 一次性获取网络 IO 事件

步骤 2:读取数据及协定解析

步骤 3:解析胜利后进行业务逻辑解决,而后应答客户端

1.2 该网络线程模型缺点

  1. 所有工作都由一个线程执行,包含 epoll 事件获取、事件处理(数据读写)、只有任一一个申请的事件回调解决阻塞,其余申请都会阻塞。例如 redis 的 hash 构造,如果 filed 过多,假如一个 hash key 蕴含数百万 filed,则该 Hash key 过期的时候,整个 redis 阻塞。
  2. 单线程工作模型,CPU 会成为瓶颈,如果 QPS 过高,整个 CPU 负载会达到 100%,时延抖动厉害。

1.3 典型案例

  1. redis 缓存
  2. 推特缓存中间件 twemproxy

1.4 主循环工作流程

while (1) {
    //epoll_wait 期待网络事件,如果有网络事件则返回,或者超时范畴
    size_t numevents=  epoll_wait();

    // 遍历后面 epoll 获取到的网络事件,执行对应事件回调
    for (j = 0; j < numevents; j++) {if(读事件) {
            // 读数据
            readData()
// 解析
parseData()
            // 读事件处理、读到数据后的业务逻辑解决
            requestDeal()} else if(写事件) {
            // 写事件处理,写数据逻辑解决
            writeEentDeal()} else {
// 异样事件处理
errorDeal()}
     }
}

阐明:后续多线程 / 过程模型中,每个线程 / 过程的主流程和该 while()流程统一。

1.5 redis 源码剖析及异步网络 IO 复用精简版 demo

因为之前工作须要,须要对 redis 内核做二次优化开发,因而对整个 redis 代码做了局部代码正文,同时把 redis 的网络模块独立进去做成了简略 demo,该 demo 对了解 epoll 网络事件处理及 Io 复用实现会有帮忙,代码比拟简短,能够参考如下我的项目:

redis 源码具体正文剖析

redis 网络模块精简版 demo

推特缓存中间件 twemproxy 源码剖析实现

2. 线程模型二. 单 listener+ 固定 worker 线程

该线程模型图如下图所示:

阐明:

  1. listener 线程负责承受所有的客户端链接
  2. listener 线程每接管到一个新的客户端链接产生一个新的 fd,而后通过散发器发送给对应的工作线程(hash 形式)
  3. 工作线程获取到对应的新链接 fd 后,后续该链接上的所有网络 IO 读写都由该线程解决
  4. 假如有 32 个链接,则 32 个链接建设胜利后,每个线程均匀解决 4 个链接上的读写、报文解决、业务逻辑解决

2.1 该网络线程模型缺点

  1. 进行 accept 解决的 listener 线程只有一个,在霎时高并发场景容易成为瓶颈
  2. 一个线程通过 IO 复用形式解决多个链接 fd 的数据读写、报文解析及后续业务逻辑解决,这个过程会有重大的排队景象,例如某个链接的报文接管解析结束后的外部解决工夫过长,则其余链接的申请就会阻塞排队

2.2 典型案例

memcache 缓存,实用于外部解决比拟快的缓存场景、代理两头场景。memcache 源码实现中文剖析能够详见: memcache 源码实现剖析

3. 线程模型三. 固定 worker 线程模型(reuseport)

该模型原型图如下:

阐明:

  1. Linux kernel 3.9 开始反对 reuseport 性能,内核协定栈每获取到一个新链接主动平衡分发给用户态 worker 线程。
  2. 该模型解决了模型一的 listener 单点瓶颈问题,多个过程 / 线程同时做为 listener,都能够 accept 客户端新链接。

3.1 该网络过程 / 线程模型缺点

reuseport 反对后,内核通过负载平衡的形式散发不同新链接到多个用户态 worker 过程 / 线程,每个过程 / 线程通过 IO 复用形式解决多个客户端新链接 fd 的数据读写、报文解析、解析后的业务逻辑解决。每个工作过程 / 线程同时解决多个链接的申请,如果某个链接的报文接管解析结束后的外部解决工夫过长,则其余链接的申请就会阻塞排队。

该模型尽管解决了 listener 单点瓶颈问题,然而工作线程外部的排队问题没有解决。

不过,Nginx 作为七层转发代理,因为都是内存解决,所以外部解决工夫比拟短,所以实用于该模型。

3.2 典型案例

  1. nginx(nginx 用的是过程,模型原理一样),该模型实用于外部业务逻辑简略的场景,如 nginx 代理等
  2. reuseport 反对性能晋升过程能够参考另一篇分享:Nginx 多过程高并发、低时延、高牢靠机制在缓存(redis、memcache)twemproxy 代理中的利用

另,参考 nginx 源码中文正文剖析

4. 线程模型四:一个链接一个线程模型

该线程模型图如下图:

阐明:

  1. listener 线程负责承受所有的客户端链接
  2. listener 线程每接管到一个新的客户端链接就创立一个线程,该线程只负责解决该链接上的数据读写、报文解析、业务逻辑解决。

4.1 该网络线程模型缺点:

  1. 一个链接创立一个线程,如果 10 万个链接,那么就须要 10 万个线程,线程数太多,零碎负责、内存耗费也会很多
  2. 当链接敞开的时候,线程也须要销毁,频繁的线程创立和耗费进一步减少零碎负载

4.2 典型案例:

  1. mysql 默认形式、mongodb 同步线程模型配置,实用于申请解决比拟耗时的场景,如数据库服务
  2. Apache web 服务器,该模型限度了 apache 性能,nginx 劣势会更加显著

5. 线程模型五:单 listener+ 动静 worker 线程(单队列)

该线程模型图如下图所示:

阐明:

  1. listener 线程接管到一个新链接 fd 后,把该 fd 交由线程池解决,后续该链接的所有读写、报文解析、业务解决都由线程池中多个线程解决。
  2. 该模型把一次申请转换为多个工作 (网络数据读写、报文解析、报文解析后的业务逻辑解决) 入队到全局队列,线程池中的线程从队列中获取工作执行。
  3. 同一个申请拜访被拆分为多个工作,一次申请可能由多个线程解决。
  4. 当工作太多,零碎压力大的时候,线程池中线程数动静减少
  5. 当工作缩小,零碎压力缩小的时候,线程池中线程数动静缩小

5.1 工作线程运行工夫相干的几个统计:

T1: 调用底层 asio 库接管一个残缺 mongodb 报文的工夫

T2: 接管到报文后的后续所有解决(含报文解析、认证、引擎层解决、发送数据给客户端等)

T3: 线程期待数据的工夫(例如:长时间没有流量,则当初期待读取数据)

5.2 单个工作线程如何判断本人处于”闲暇”状态:

线程运行总工夫 =T1 + T2 +T3,其中 T3 是无用等待时间。如果 T3 的无用等待时间占比很大,则阐明线程比拟闲暇。工作线程每一次循环解决后判断无效工夫占比,如果小于指定阀值,则本人间接 exit 退出销毁

5.3 如何判断线程池中工作线程“太忙”:

控制线程专门用于判断线程池中工作线程的压力状况,以此来决定是否在线程池中创立新的工作线程来晋升性能。

控制线程每过肯定工夫循环查看线程池中的线程压力状态,实现原理就是简略的实时记录线程池中的线程以后运行状况,为以下两类计数:总线程数_threadsRunning、以后正在运行 task 工作的线程数_threadsInUse。如果_threadsRunning=_threadsRunning,阐明所有工作线程以后都在解决 task 工作,线程池中线程压力大,这时候控制线程就开始减少线程池中线程数。

该模型具体源码实现过程更多细节咱们之前公布的文章:MongoDB 网络传输解决源码实现及性能调优——体验内核性能极致设计

5.4 该网络线程模型缺点:

  1. 线程池获取工作执行,有锁竞争,这里就会成为零碎瓶颈

5.5 典型案例:

mongodb 动静 adaptive 线程模型,实用于申请解决比拟耗时的场景,如数据库服务。

该模型具体源码优化剖析实现过程参考:
Mongodb 网络传输解决源码实现及性能调优 - 体验内核性能极致设计

6. 线程模型六. 单 listener+ 动静 worker 线程(多队列)-mongodb 网络线程模型优化实际

该线程模型图如下:

阐明:

把一个全局队列拆分为多个队列,工作入队的时候依照 hash 散列到各自的队列,工作线程获取获取工作的时候,同理通过 hash 的形式去对应的队列获取工作,通过这种形式缩小锁竞争,同时晋升整体性能。

6.1 典型案例:

OPPO 自研 mongodb 内核多队列 adaptive 线程模型优化,性能有很好的晋升,实用于申请解决比拟耗时的场景,如数据库服务。

该模型具体源码优化剖析实现过程同样参考:Mongodb 网络传输解决源码实现及性能调优 - 体验内核性能极致设计

6.2 疑难?为啥 mysql、mongodb 等数据库没有利用内核的 reuseport 非凡 - 多线程同时解决 accept 申请?

答:实际上所有服务都能够利用这一个性,包含数据库服务(mongodb、mysql 等)。然而因为数据库服务拜访时延个别都是 ms 级别,如果 reuseport 个性利用起来,时延会有几十 us 的性能晋升,这相比数据库外部解决的 ms 级时延,这几十 us 的性能晋升,基本上能够疏忽掉,这也是大部分数据库服务没有反对该性能的起因。

缓存,代理等中间件,因为自身外部解决工夫就比拟小,也是 us 级别,所以须要充分利用该个性。

正文完
 0