写点什么

搞会这个索引添加法,十亿级时延敏感集群想抖动都难

  • 2021-06-22
  • 本文字数:3994 字

    阅读完需:约 13 分钟

搞会这个索引添加法,十亿级时延敏感集群想抖动都难

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


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


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


  • 为何 background 后台加索引会引起时延敏感集群抖动?

  • 为何前面两个索引添加过程没触发告警,第三个索引添加完成后才触发告警?

  • 为何只有从节点抖动,主节点时延一切正常?

  • 为何连接数暴涨?

  • 连接数耗光,mongo shell 无法登陆查看节点内部状态信息,如何破局?

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

一、业务背景

某业务存储公司核心数据,集群异常会影响公司流水收入,该业务对时延非常敏感,稍有抖动就容易引起客户端超时异常,该业务场景如下:


  • 数据量很小,10 亿级

  • 核心业务

  • 时延敏感

  • 分片模式,单个分片

  • 读写分离

  • 读多写少

  • 峰值流量 8-10W/s


该集群对应 mongodb 内核版本:3.6.13,某天业务自己通过 mongodb 管控平台串行方式添加几个索引(background 后台添加),一个索引添加执行完成返回后,业务开始下一个索引的添加。


添加第一个索引和第二个索引完成后,业务没告警,但是当业务添加完第三个索引后,开始收到部分查询时延超过阀值告警。

二、集群架构

2.1 集群部署架构

该集群部署架构如下:



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



如上图所示,该业务部署只有一个分片,该分片为一主四从结构 5 节点。分片 1 采用 5 节点的原因如下:


  • 核心业务,5 副本方式部署,可以容忍两个节点估值

  • 时延敏感,由于业务优先读从节点,因此可以通过增加分片从节点的方式提升业务的 QPS。

2.2 一个分片为何要选择分片模式?复制集不是可以满足要求吗?

从上面的结构图可以看出,该集群只有一个分片,采用了分片模式架构,为何不选择复制集架构,这样还可以省掉 mongos 代理和 config server 的成本开销。采用分片模式主要基于如下因素考虑:


  • 该业务当前数据比较小,10 亿级别,但是随着时间增长后续可能会增加到百亿级别,考虑到以后可能存在分片扩容的需求,因此采用了分片模式。

  • 该集群当前写入更新比较少,后续可能存在大量写入更新的场景,大量写入更新需要多分片来支撑。

  • 我司在 mongos 代理增加了很多功能,例如限流、流量控制、权限细化控制、监控信息完善等功能,因此默认采用分片模式。

三、问题快速发现及解决

3.1 问题发现

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



几乎四个从节点先后收到同样的告警,节点时延部分请求超过 20ms,由于该业务是非常核心的影响业务营收的核心集群,非常紧张。但是,有一个很奇怪的现象,主节点访问时延正常,只有从节点时延抖动。


此外,还不停收到实例不可用异常告警,对应监控曲线如下:




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

3.2 问题排查过程 

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


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


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


1.MongoDB shell version v3.6.13  2.connecting to: mongodb://x.x.x.x:20001/test?gssapiServiceName=mongodb  3.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'  :  4.connect@src/mongo/shell/mongo.js:263:13  5.@(connect):1:6  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 

四、createIndex 构建索引核心流程及问题暴露过程

4.1 createIndex 构建索引核心流程

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



主节点接受到 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。


总结如下图所示:


五、疑问解答

  • 为何 background 后台加索引会引起时延敏感集群抖动?

如上面分析,虽然业务是串行的方式一个索引添加成功后再添加下一个 background 后台索引,由于主从索引构建执行时间的长短不同,从节点通过拉取对应 oplog 重放,最终引起某一时刻开始三个索引在所有从节点同时运行,引起 IO 负载很高,最终触发业务访问时延告警。


  • 为何前面两个索引添加过程没触发告警,第三个索引添加完成后才触发告警?

如上,从节点拉取 Oplog 获取到第三个索引执行的时候 IO 负载进一步增加,最终触发了 20ms 访问时延阀值。


  • 为何只有从节点抖动,主节点时延一切正常?

主节点由于业务添加是一个索引后台添加完成后,才添加第二个索引。也就是主节点同一时刻只会有一个索引在执行,IO 负载低,此外由于主节点写流量本身不高,读流量几乎都在从节点,索引加索引执行很快,并且几乎不会影响写流量时延。


  • 为何连接数暴涨?

连接数暴涨实际上是加索引引起业务访问慢的结果,由于三个索引同时在从节点构建索引运行,造成从节点 IO 负载很高,最终造成业务访问变慢。


访问变慢后,会引起客户端链接池中的链接不够用,于是客户端会动态的增加链接池中的连接数来进行后端 DB 访问,最终造成了 mongod 服务端连接数到达配置上线出现无法链接的问题。


  • 连接数耗光,mongo shell 无法登陆查看节点内部状态信息,如何破局?

连接数耗光,mongo shell 将无法连接节点,无法获取节点内部状态。可以对该功能做优化,对指定的客户端(默认 127.0.0.1)设置白名单,取消 max connections 限制,这样我们即可通过节点本机登陆 mongod 后台获取内部状态信息。


例如增加了链接限制白名单后,就可以通过 127.0.0.1 登陆到节点内部,然后通过 killOp 操作把从节点正在构建索引的操作干掉。

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

方法一:所有主从确保索引执行完成后添加下一个索引(影响相对较小)后台 background 加索引,确保所有主从索引构建完成后,才开始下一个索引的创建,避免出现本文所说的多个索引同时在从节点执行引起业务抖动。


说明:mongodb 高版本中对后台添加索引做了优化,从节点拉取建索引对应 oplog 重放的时候,只有第一个索引执行完成,才会执行第二个索引,从而避免了同时多个索引同时执行引起的抖动。


方法二:单机启动,然后加索引,加完索引后再加入到副本集(业务无任何感知)无感知添加索引步骤如下:


  • 从复制集中移除某个从节点

  • 单机方式启动该节点

  • 阻塞方式(不带 background)加索引,这样索引构建速度更快

  • 索引添加完成后,副本集方式启动该节点

  • 把该节点加入复制集


通过以上步骤,即可无感知方式完成一个从节点的索引添加,其他节点添加过程重复该操作过程即可。


作者介绍

杨亚洲,前滴滴出行专家工程师,现任 OPPO 文档数据库 mongodb 负责人,负责数万亿级数据量文档数据库 mongodb 内核研发、性能优化及运维工作,一直专注于分布式缓存、高性能服务端、数据库、中间件等相关研发。


本文转载自:dbaplus 社群(ID:dbaplus)

原文链接:搞会这个索引添加法,十亿级时延敏感集群想抖动都难

2021-06-22 07:001611

评论

发布
暂无评论
发现更多内容

从输入URL到渲染的过程中到底发生了什么?

loveX001

JavaScript

vue为什么v-for的优先级比v-if的高?

bb_xiaxia1998

Vue

【SSM】Spring系列——AOP面向切面编程

胖虎不秃头

spring ssm 10月月更

2022-10-03:给定一个正数n,比如6 表示数轴上有 0,1,2,3,4,5,6 <0 或者 >6 的位置认为无法到达 给定两个数字x和y,0<= x,y <= n 表示小人一开始在x的位置,它

福大大架构师每日一题

算法 rust 福大大

React的useLayoutEffect和useEffect执行时机有什么不同

beifeng1996

React

Goland嗖嗖的: 快捷键,自动生成代码等效率小技巧

琦彦

Go goland 10月月更 live template

【愚公系列】2022年10月 Go教学课程 017-分支结构之IF

愚公搬代码

10月月更

大数据ELK(十二):Elasticsearch编程(环境准备)

Lansonli

ES 10月月更

React循环DOM时为什么需要添加key

beifeng1996

React

vue面试之Composition-API响应式包装对象原理

bb_xiaxia1998

Vue

同事嫌我改Bug慢,原来是没掌握这些代码Debug技巧

慕枫技术笔记

debug 后端 10月月更

【SSM】Spring系列——Spring集成MyBatis

胖虎不秃头

spring ssm 10月月更

基于kube-scheduler-simulator编写自己的调度程序

琦彦

k8s 调度 kube-scheduler 10月月更

【SSM】SpringMVC系列——SpringMVC概述

胖虎不秃头

spring ssm 10月月更

js函数式编程讲解

hellocoder2029

JavaScript

存储优化--查询分离

喵叔

10月月更

【深度讲解系列】SpringBoot入门

Geek_65222d

10月月更

微服务标准化

穿过生命散发芬芳

微服务 10月月更

架构师的十八般武艺

agnostic

构架师

你是怎样解决跨域问题的?-面试必问

loveX001

JavaScript

Vue.$nextTick的原理是什么-vue面试进阶

bb_xiaxia1998

Vue

【一Go到底】第三天---变量的使用

指剑

Go golang 10月月更

Docker下的Nacos环境开发

程序员欣宸

Docker nacos spring cloud alibaba 10月月更

Angular 内容投影出现 No provider for TemplateRef found 错误的单步调试

汪子熙

typescript Web 前端开发 angular 10月月更

【LeetCode】使括号有效的最少添加Java题解

Albert

LeetCode 10月月更

Collections之Arraylist源码解读(四)

知识浅谈

ArrayList 10月月更

MFC|双缓存绘图机制

中国好公民st

c++ qt 10月月更

数据产品经理实战-项目管理

第519区

项目管理 数据产品经理

Java线上惨痛踩坑记录,你也一定遇到过

一灯架构

Java java面试 10月月更

分享Go书籍-《Go Web编程》

沙漠尽头的狼

网络爬虫与http+ssl(1)

张立梵

Python. 爬虫必备知识讲解 10月月更

搞会这个索引添加法,十亿级时延敏感集群想抖动都难_大数据_dbaplus社群_InfoQ精选文章