本文来自OPPO互联网技术团队,转载请注名做者。同时欢迎关注OPPO互联网技术团队的公众号:OPPO_tech,与你分享OPPO前沿互联网技术及活动。html
前情提要:前不久OPPO互联网技术推出《OPPO百万级高并发MongoDB集群性能数十倍提高优化实践(上)》一文,广受好评,现推出下篇以飨读者。未读过上篇的朋友能够先读上篇,了解问题的背景及优化方法,这样能够更好的了解和学习下篇内容。ios
线上某集群峰值TPS超过100万/秒左右(主要为写流量,读流量很低,读写流量作了主从读写分离,读流量走从节点,qps数百上千),峰值tps几乎已经到达集群上限,同时平均时延也超过100ms,随着读写流量的进一步增长,时延抖动严重影响业务可用性。mongodb
该集群采用MongoDB自然的分片模式架构,数据均衡的分布于各个分片中,添加片键启用分片功能后实现完美的负载均衡。集群每一个节点流量监控以下图所示:数据库
从上图能够看出集群流量比较大,峰值已经突破120万/秒,其中delete过时删除的流量不算在总流量里面(delete由主触发删除,可是主上面不会显示,只会在从节点拉取oplog的时候显示)。若是算上主节点的delete流量,总tps超过150万/秒。性能优化
在《OPPO百万级高并发MongoDB集群性能数十倍提高优化实践(上)》中,咱们经过业务优化、MongoDB服务层配置优化、Wiredtiger存储引擎层优化及硬件IO优化后,客户端整体时延从几百ms迟控制到了2-5ms左右,整体性能有了很大的提高,可是当有超大流量写冲击的时候,会有几十ms时延抖动,几个不一样接口的时延以下图所示:bash
在《上》中,咱们经过定位nvme ssd硬件的IO问题后,和厂商一块儿分析后发现IO问题是由于操做系统版本不对引发,因而开始对线上的主从MongoDB实例的服务器硬件进行升级,升级后开始替换线上该集群的实例。服务器
具体操做过程以下:架构
为了验证IO升级后的机器,咱们替换一个分片的从节点为升级后的服务器(IO问题得以解决,IO能力从以前的500M/s写入达到了近2G/s,咱们称IO升级后的服务器为高IO服务器,未升级的服务器为低IO服务器),替换后经过iostat能够看到该从节点的IO 100%问题获得了很大程度的缓解,不会出现持续性IO跌0问题。并发
第一步上面的服务器跑了一周后,咱们肯定升级后的高IO服务器运行稳定,为了谨慎起见,咱们虽然肯定该高IO服务器在从节点运行没有问题,可是咱们须要进一步在主节点验证是否文档,因而咱们作了一次主从切换,该高IO服务器变为主节点运行,也就是集群中某个分片的主节点为高IO服务器,可是从节点仍是低IO服务器。负载均衡
当高IO服务器在某个分片的主节点跑了数周后,咱们肯定高IO服务器在主节点运行正常,因而咱们得下结论:升级后的服务器运行稳定。
肯定高IO服务器没问题后,咱们开始批量替换MongoDB实例到该服务器。为了保险起见,毕竟只验证了一台高IO服务器在主从运行都没问题,因而咱们考虑只把整个集群的主节点替换为高IO服务器(由于当时认为客户端都是用的默认配置,数据写到主节点就会返回OK,虽然从节点IO慢,可是仍是能够追上oplog速度的,这样客户端时延就会很好的获得控制)。
为了谨慎保险起见,经过上面的硬件替换升级过程,咱们只替换了全部分片的主节点,提后先后架构发生了变化,原有集群硬件架构以下图所示:
全部分片主节点硬件升级后的架构图以下图所示:
从上图可知,新的集群架构,主从节点服务器IO能力有比较大的差距。最开始咱们认为业务方默认没有设置WriteConncern,也就是默认写入到Primary就向客户端发送确认,所以不会影响业务写入。
全部分片主节点升级为高IO服务器后,多个业务接口的时间访问延迟降到了平均2-4ms左右,可是在超大流量冲击的时候,仍是有几十ms的尖刺,我选取一个接口的时延为例,以下图所示:
从上图能够看出,特别是在大流量冲击的时间点,尖刺越明显。
在上一节,咱们替换了分片的全部主节点为高IO服务器,从节点仍是之前未升级的低IO服务器。因为业务方默认没有设置WriteConncern,所以咱们认为客户端写到主成功就会返回客户端OK,即便从服务器性能差也不会影响客户端写主。
在升级主服务器后,继续优化存储引擎把eviction_dirty_trigger: 25%
调整到了30%。
因为在超大流量的高并发冲击,会从平峰期的几十万TPS瞬间飙升到百万级别,并且该毛刺几乎天天都会出现两三次,比较容易复现。因而提早部署好Mongostat监控全部实例,同时在每一个服务器上用Iostat监控实时的IO情况,同时编写脚本实时采集db.serverstatus()
、db.printSlaveReplicationInfo()
、db.printReplicationInfo()
等集群重要信息。
当某个时间点监控出现毛刺后,因而开始分析Mongostat,咱们发现一个问题,即便在平峰期,脏数据比例也会持续增加到阀值(30%),咱们知道当脏数据比例超过eviction_dirty_trigger:30%
阀值,用户线程就会进行evict淘汰,这样用户线程就会阻塞直到腾出内存空间,所以淘汰刷盘过程很慢。分析平峰期毛刺时间点对应的Mongostat监控,发现以下状况:
从上图能够看出,集群TPS才40-50万左右的时候某个分片的主节点出现了脏数据达到eviction_dirty_trigger: 30%
阀值,因而整个集群访问时延就会瞬间增长,缘由是一个分片的用户线程须要刷盘,致使这个分片的访问时延上升(实际上其余分片的访问时延仍是正常的),最终把总体平均时延拉上去了。
为何普通平峰期也会有抖动?这很明显不科学。
因而获取出问题的主节点的一些监控信息,得出如下结论:
IO正常,IO不是瓶颈。
分析抖动的时候的系统top负载,负载正常。
该分片的TPS才4万左右,显然没到到分片峰值。
db.printSlaveReplicationInfo()
看到主从延迟较高。
当客户端时延监控发现时间延迟尖刺后,咱们发现主节点全部现象一切正常,系统负载、IO、TPS等都没有到达瓶颈,可是有一个惟一的异常,就是主从同步延迟持续性增长,以下图所示:
同时对应低IO服务器的从节点上面的IO情况以下图:
从节点的IO性能一塌乌涂,这也正式主从延迟增长的根源。
从上图能够看出在时延尖刺的一样时间点,主从延迟超大。因而怀疑时延尖刺可能和从节点拉取Oplog速度有关系,因而把整个Mongostat
、iostat
、top
、db.printSlaveReplicationInfo()
、db.serverstatus()
等监控持续跑了两天,记录下了两天内的一些核心系统和Mongo监控指标。
两天后,对着客户端时延尖刺时间点分析对应监控数据,发现一个共同的现象,尖刺出现时间点和脏数据eviction_dirty_trigger
超过阀值时间点一致,同时主从延迟在这个时间点都有很大的延迟。
到这里,咱们愈来愈怀疑问题和从节点拉取oplog速度有关。以前认为业务方默认没有设置WriteConncern,也就是默认写入到Primary就向客户端发送确认,可能应答客户端前还有其余流程会影响写入。因而查看MongoDB-3.6的Production Notes,从中发现了以下信息:
从Production Notes能够看出,MongoDB-3.6默认启用了 read concern "majority"功能,因而怀疑抖动可能和该功能有关。
为了不藏独,MongoDB增长了该功能,启用该功能后,MongoDB为了确保带有带有参数readConcern("Majority")
的客户端读取到的数据确实是同步到大多数实例的数据,所以MongoDB必须在内存中借助snapshot 及主从通讯来维护更多的版本信息,这就增长了Wiredtiger存储引擎对内存的需求。
因为从节点是低IO服务器,很容易形成阻塞,这样拉取oplog的速度就会跟不上进度,形成主节点消耗大量的内存来维护快照信息,这样就会致使大量的内存消耗,最终致使脏数据瞬间剧增,很快达到eviction_dirty_trigger
阀值,业务也所以抖动。
说一个小插曲,由于MongoDB-3.6默认开启enableMajorityReadConcern
功能,咱们在这个过程当中出现过一次严重的集群故障,业务流量有段时间忽然暴涨,形成时延持续性达到几千ms,现象以下:
该问题的根源也是由于enableMajorityReadConcern
功能引发,因为从节点严重落后主节点,致使主节点为了维护各类snapshot快照,消耗大量内存,同时从节点和主节点的oplog延后,致使主节点维护了更多的内存版本,脏数据比例持续性增加,直到从节点追上oplog。因为咱们的业务不须要readConcert功能,所以咱们考虑禁用该功能(配置文件增长配置replication.enableMajorityReadConcern=false
。
鉴于篇幅,enableMajorityReadConcern
及主从硬件IO能力不足引发的严重业务故障,本篇不作详细的分析,后期会写一篇专门的 《百万计高并发MongoDB集群性能优化采坑记》 作分享,除了ReadConcern采坑,还有其余好几个核心采坑点,敬请关注。
此外,后续会专门写一篇ReadConcern的原理及代码实现分析文章,敬请关注。
除了经过replication.enableMajorityReadConcern=false
在配置文件中禁用ReadConcern Majority功能,咱们继续把全部分片的从节点由以前的低IO服务器替换为升级后的高IO服务器,升级后全部主从硬件资源性能彻底同样,升级后集群分片架构以下图所示:
经过禁用功能,并统一主从服务器硬件资源后,查看有抖动的一个接口的时间延迟,以下图所示:
从上图能够看出,经过MajorityReadConcern
功能而且替把全部从节点的低IO服务器作系统升级后,业务时间延迟抖动的峰值进一步下降了,从以前第2节中的峰值80ms下降到了如今的峰值40ms左右。
此外,3.1节提到的脏数据持续性突破eviction_dirty_trigger
阀值引发客户端时延飙涨到几千ms的问题得以完全解决。
经过前面的条优化,咱们发现有一个业务接口仍是偶尔有40ms时延尖刺,分析发现主要是eviction_dirty_trigger
达到了咱们配置的阀值,业务线程开始淘汰page cache,这样就形成业务线程很慢,最终致使平均时延尖刺。
为了进一步减缓时延尖刺,咱们继续在以前基础上对存储引擎调优,调整后配置以下:
eviction_target: 75%
eviction_trigger:97%
eviction_dirty_target: %3
eviction_dirty_trigger:30%
evict.threads_min:12
evict.threads_max:18
checkpoint=(wait=20,log_size=1GB)
复制代码
通过此轮的存储引擎调优后,该业务的核心接口时延进一步好转,时延尖刺相比以前有了进一步的改善,时延最大尖刺时间从前面的40ms下降到了30ms,同时尖刺出现的频率明显下降了,以下图所示:
从上图能够看出,在峰值TPS百万级别的时候,部分节点evict淘汰速率已经跟不上写入速度,所以出现了用户线程刷盘的状况。
但很奇怪的是,在这个时间点分析对应机器的系统负载、IO情况、内存情况等,发现系统负载、比较正常,可是对应服务器IO偏高,以下图所示:
同时分析对应服务器对应时间点的慢日志,咱们发现尖刺出现时间的的慢日志统计以下:
分析非时延尖刺时间点,对应慢日志统计以下:
分析两个时间点慢日志能够看出,慢日志出现的条数和时间延迟尖刺出现的时间点一致,也就是IO负载很高的时候。
经过上面的分析能够看出,当IO比较高(util超过50%)的时候,慢日志和时延都会增长,他们之间成正比关系。
经过软件层面(MongoDB服务配置、业务优化、存储引擎优化)及硬件优化(升级操做系统)后,该大流量集群的核心接口时延从最初的平均成百上千ms下降到了如今的平均1-2ms,性能提高比较可观,总体时延性能提高数十倍。
优化前主要接口时延:
在不增长物理机器的基础上,通过一系列的优化措施,最终业务方主要接口时延控制到了几ms,以下图所示:
预告
近期咱们还将继续分享以下主题,敬请关注:
百万计高并发MongoDB集群性能优化采坑记
线上典型集群抖动、不可用等问题汇总分析
MongoDB文档数据库业务使用最佳案例分享
最后的最后,顺便打一个广告,OPPO互联网运维云存储团队急缺多个岗位:
若是对MongoDB内核源码、Wiredtiger存储引擎、RocksDB存储引擎、数据库机房多活、数据链路同步系统、中间件、数据库等有兴趣的同窗。欢迎加入OPPO你们庭,一块儿参与OPPO百万级高并发文档数据库研发。
工做地点:成都 / 深圳
邮箱:yangyazhou#oppo.com