5

记一次十亿级时延敏感集群加索引引起的业务抖动及快速恢复方法

 2 years ago
source link: https://mongoing.com/archives/79832
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

线上某MongoDB集群存储影响公司收入流水的核心数据,本文分享该集群为何多个索引串行后台会引起集群抖动,并且部分节点出现了连接数耗光等问题。同时通过本案例,给出时延敏感业务该最优方式添加索引,做到对业务最小化影响或者无影响。

索引对业务查询性能提升起着至关重要的作用,但是绝大部分MongoDB程序员和DBA对时延敏感业务的索引添加方法是错误的。

本文主要完成一下几个目的:

  • 为何backgroud后台加索引会引起时延敏感集群抖动?
  • 为何前面两个索引添加过程没触发告警,第三个索引添加完成后才触发告警?
  • 为何只有从节点抖动,主节点时延一切正常?
  • 为何连接数暴涨?
  • 连接数耗光,mongo shell无法登陆查看节点内部状态信息,如何破局?
  • 时延敏感型业务如何做到业务无感知索引添加?

1. 业务背景

某业务存储公司核心数据,集群异常会影响公司流水收入,该业务对时延非常敏感,稍有抖动就容易引起客户端超时异常,该业务场景如下:
  • 数据量很小,10亿级
  • 分片模式,单个分片
  • 峰值流量8-10W/s
该集群对应MongoDB内核版本:3.6.13,某天业务自己通过MongoDB管控平台串行方式添加几个索引(backgroud后台添加),一个索引添加执行完成返回后,业务开始下一个索引的添加。添加第一个索引和第二个索引完成后,业务没告警,但是当业务添加完第三个索引后,开始收到部分查询时延超过阀值告警。

2. 集群架构

2.1 集群部署架构

该集群部署架构如下:

该业务集群对应流量监控曲线如下:

如上图所示,该业务部署只有一个分片,该分片为一主四从结构5节点。分片1采用5节点的原因如下:① 核心业务,5副本方式部署,可以容忍两个节点估值② 时延敏感,由于业务优先读从节点,因此可以通过增加分片从节点的方式提升业务的QPS。

2.2 一个分片为何要选择分片模式?

一个分片为何要选择分片模式?复制集不是可以满足要求吗?从上面的结构图可以看出,该集群只有一个分片,采用了分片模式架构,为何不选择复制集架构,这样还可以省掉mongos代理和config server的成本开销。采用分片模式主要基于如下因素考虑:① 该业务当前数据比较小,10亿级别,但是随着时间增长后续可能会增加到百亿级别,考虑到以后可能存在分片扩容的需求,因此采用了分片模式。② 该集群当前写入更新比较少,后续可能存在大量写入更新的场景,大量写入更新需要多分片来支撑。③ 我司在mongos代理增加了很多功能,例如限流、流量控制、权限细化控制、监控信息完善等功能,因此默认采用分片模式。

3. 问题快速发现及解决

3.1 问题发现

某天,突然告警中心打来电话,突然收到如下告警信息:

几乎四个从节点先后收到同样的告警,节点时延部分请求超过20ms,由于该业务是非常核心的影响业务营收的核心集群,非常紧张。但是,有一个很奇怪的现象,主节点访问时延正常,只有从节点时延抖动。此外,还不停收到实例不可用异常告警,对应监控曲线如下:

说明:上图曲线一根代表客户端当前已用连接数,一根曲线代表剩余可用连接数。

3.2 问题排查过程

收到告警后,发现业务有很多慢日志(时延敏感业务,慢日志打印阀值为20ms),同时慢日志都走了最优索引。

  • 通过mongo shell登陆对应节点后台

于是通过mongo shell登陆节点后台,但是登陆不上,出现如下打印:

MongoDB shell version v3.6.13  
connecting to: MongoDB://x.x.x.x:20001/test?gssapiServiceName=MongoDB  
2021-04-29T11:09:15.049+0800 E QUERY    [thread1] Error: network error while attempting to run command 'isMaster' on host x.x.x.x:20001'  :  
connect@src/mongo/shell/mongo.js:263:13  
@(connect):1:6  exception: connect failed

由于节点登陆不上,因此登陆到存储节点查看后台日志,日志中有大量的打印提示连接数耗光了,如下图:

  • 节点系统监控统计分析
从上面的现象可以看出链接耗光了,于是分析节点所在服务器系统监控,发现一个问题,磁盘IO非常高,如下:
  • 分析mongod实例日志
由于从节点登陆不上,系统磁盘IO很高,因此怀疑有慢操作在运行,于是分析实例日志,发现如下现象:
  • 问题确认
通过前面的分析可以得出问题根因在于加索引引起从节点磁盘IO过高,最终引起业务查询时延上升抖动。通过和业务沟通,业务这段时间确实通过我们的管控平台串行方式加了几个索引,磁盘IO过高由业务加索引引起,同时从节点同一时刻有多个索引添加。加索引过程首先需要读取表数据,然后通过数据构建索引,这个过程都会有多次IO操作。磁盘IO是公用的,服务器IO高会引起该服务器上所有的IO操作变慢,因此最终引起从节点读服务抖动。
  • 问题解决过程
到这里,我们已经确定问题是由于加索引引起,只有把索引干掉磁盘IO才会恢复正常,因此我们需要尽快干掉索引。然而,由于连接数已经耗光,无法链接从节点,所以我们不能做killop操作。由于无法登陆后台做killop操作,于是直接kill进程,kill进程后启动,发现mongod还是在构建索引,如下:

重启后,还是需要构建索引,因为之前索引没有执行完成mongod进程就挂了,因此需要重建索引来保持与主节点状态一直。不过,mongod为了解决类似问题,提供了一个noIndexBuildRetry参数来跳过实例加索引中途异常重启后重构索引的流程,该参数功能如下说明:    don’t retry any index builds that were interrupted by shutdown       noIndexBuildRetry放弃启动从节点mongod实例,业务很快恢复:mongod -f  /home/service/MongoDB/conf/mongod_20001.conf–noIndexBuildRetry

4. createIndex构建索引核心流程及问题暴露过程

4.1 createIndex构建索引核心流程

业务链接代理通过createIndex命令添加backgroud后台索引,其运行流程如下图所示:

主节点接受到createIndex命令后的执行主要流程如下:① 主节点查询对应表数据,然后build构建索引。② 索引数据构建执行完成后,返回客户端OK。(注意:主构建完成后就通知OK给客户端,实际上这时候从节点还没有开始构建索引)③ 生成createIndex对应oplog数据到oplog表④ 从节点获取到createIndex对应oplog操作,然后重放createIndex构建索引。

4.2 问题暴露流程

通过分析日志时间点和告警时间点,和业务确认,发现当业务第三个索引添加完成后(实际上只是主节点构建索引完成),开始触发时延告警阀值。总接时间序列如下:

  • T1时刻第一个索引主节点构建完成,然后同步到两个从节点构建索引,也就是T1时刻两个从节点只有一个索引index1在运行。
  • T2时刻第二个索引主节点构建完成,然后从节点获取到这个索引执行,这时候由于从节点读流量大,因此构建索引比主节点慢,最终index1和index2都在两个从节点运行。此时,访问时延还没有触发时延告警阀值。
  • 以此类推,T3时刻第三个索引添加完成,从节点通过oplog获取到第三个索引运行,由于此时index1、index2都还没有运行完成,因此两个从节点同时构建index1、index2和index3索引。三个索引的同时运行,进一步加重了磁盘IO负载和系统开销,业务访问时延进一步上升,最终造成部分查询时延超过20ms。
总结如下图所示:

5. 疑问解答

1. 为何backgroud后台加索引会引起时延敏感集群抖动?如上面分析,虽然业务是串行的方式一个索引添加成功后再添加下一个backgroud后台索引,由于主从索引构建执行时间的长短不同,从节点通过拉取对应oplog重放,最终引起某一时刻开始三个索引在所有从节点同时运行,引起IO负载很高,最终触发业务访问时延告警。2. 为何前面两个索引添加过程没触发告警,第三个索引添加完成后才触发告警?如上,从节点拉取Oplog获取到第三个索引执行的时候IO负载进一步增加,最终触发了20ms访问时延阀值。3. 为何只有从节点抖动,主节点时延一切正常?主节点由于业务添加是一个索引后台添加完成后,才添加第二个索引。也就是主节点同一时刻只会有一个索引在执行,IO负载低,此外由于主节点写流量本身不高,读流量几乎都在从节点,索引加索引执行很快,并且几乎不会影响写流量时延。4. 为何连接数暴涨?连接数暴涨实际上是加索引引起业务访问慢的结果,由于三个索引同时在从节点构建索引运行,造成从节点IO负载很高,最终造成业务访问变慢。访问变慢后,会引起客户端链接池中的链接不够用,于是客户端会动态的增加链接池中的连接数来进行后端DB访问,最终造成了mongod服务端连接数到达配置上线出现无法链接的问题。5. 连接数耗光,mongo shell无法登陆查看节点内部状态信息,如何破局?连接数耗光,mongo shell将无法连接节点,无法获取节点内部状态。可以对该功能做优化,对指定的客户端(默认127.0.0.1)设置白名单,取消max connections限制,这样我们即可通过节点本机登陆mongod后台获取内部状态信息。例如增加了链接限制白名单后,就可以通过127.0.0.1登陆到节点内部,然后通过killOp操作把从节点正在构建索引的操作干掉。

6. 时延敏感型业务如何做到业务无感知索引添加?

方法一:所有主从确保索引执行完成后添加下一个索引(影响相对较小)后台backgroud加索引,确保所有主从索引构建完成后,才开始下一个索引的创建,避免出现本文所说的多个索引同时在从节点执行引起业务抖动。说明:MongoDB高版本中对后台添加索引做了优化,从节点拉取建索引对应oplog重放的时候,只有第一个索引执行完成,才会执行第二个索引,从而避免了同时多个索引同时执行引起的抖动。方法二:单机启动,然后加索引,加完索引后再加入到副本集(业务无任何感知)无感知添加索引步骤如下:① 从复制集中移除某个从节点② 单机方式启动该节点③ 阻塞方式(不带backgroud)加索引,这样索引构建速度更快④ 索引添加完成后,副本集方式启动该节点⑤ 把该节点加入复制集通过以上步骤,即可无感知方式完成一个从节点的索引添加,其他节点添加过程重复该操作过程即可。

阅读更多:

MongoDB Command命令处理模块源码实现一MongoDB Command命令处理模块源码实现二MongoDB详细表级操作统计及详细时延统计实现原理常用高并发网络线程模型设计及MongoDB线程模型优化实践MongoDB网络传输处理源码实现及性能调优-体验内核性能极致设计OPPO百万级高并发MongoDB集群性能数十倍提升优化实践盘点 2020 | 我要为分布式数据库 MongoDB 在国内影响力提升及推广做点事MongoDB网络传输层模块源码实现二MongoDB网络传输层模块源码实现三MongoDB网络传输层模块源码实现四 

作者:杨亚洲

前滴滴出行技术专家,现任OPPO文档数据库MongoDB负责人,负责oppo千万级峰值TPS/十万亿级数据量文档数据库MongoDB内核研发及运维工作,一直专注于分布式缓存、高性能服务端、数据库、中间件等相关研发。Github账号地址:

https://github.com/y123456yz

添加小芒果微信(ID:mongingcom)进入中文用户组技术交流群。

 

Mongoing中文社区

MongoDB中文社区微信公众号

扫描关注,获取更多精彩内容

社区网站www.mongoing.com


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK