产品战略专家梁宁确认出席AICon北京站,分享AI时代下的商业逻辑与产品需求 了解详情
写点什么

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

  • 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:001377

评论

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

模块五作业:设计微博系统中”微博评论“的高性能高可用计算架构。

dean

架构实战营

react源码解析10.commit阶段

buchila11

React react源码

设计模式【4】-- 建造者模式详解

秦怀杂货店

设计模式

「阿里云可观测系列公开课」正式发布,多维度助力企业强化可观测能力

阿里巴巴云原生

阿里云 云原生 直播 可观测 公开课

设计微博评论架构

张靖

#架构实战营

极客时间

Nydia

28天写作 12月日更

中年人的沉重1

张老蔫

28天写作

大厂算法面试之leetcode精讲15.链表

全栈潇晨

算法 链表 LeetCode

刚提测就改需求,我是渣男吗?

小傅哥

Java 加班 小傅哥 需求迭代 产品功能

linux重要目录之usr和var

入门小站

Linux

react源码解析9.diff算法

buchila11

React React Diff

【Dart 专题】Generics 泛型 <T>

阿策小和尚

Flutter 小菜 0 基础学习 Flutter Android 小菜鸟 12月日更

Redis之Pipeline详解

李子捌

redis pipeline 28天写作 12月日更

岁末整理-2021

将军-技术演讲力教练

大厂算法面试之leetcode精讲16.set&map

全栈潇晨

LeetCode 算法面试

SpringCloudAliBaba之微服务常识扫盲

XiaoLin_Java

架构 微服务 springcloudAlibaba 签约计划第二季

SpringCloudAliBaba组件之Nacos精讲【注册、配置中心】

XiaoLin_Java

微服务 nacos 配置中心 springcloudAlibaba 签约计划第二季

[Pulsar] JDBC core sink connector介绍及实现

Zike Yang

Apache Pulsar 11月日更 12月日更

【LeetCode】相对名次Java题解

Albert

算法 LeetCode 12月日更

华云大咖说 | 安超信创桌面云金融行业解决方案

华云数据

python 爬虫爱好者必须掌握的知识点“ 协程爬虫”,看一下如何用 gevent 采集女生用头像

梦想橡皮擦

12月日更

手把手搭建微服务项目,他到底有什么不一样?

XiaoLin_Java

架构 微服务 springcloudAlibaba 签约计划第二季 单体项目

在线火星文转简体中文工具

入门小站

工具

微博系统中“微博评论”的高性能计算架构

波波

「架构实战营」

模块五:如何设计业务高性能高可用计算架构? --学习总结

小鹿

将远程服务像本地一样调用?Feign来帮你!

XiaoLin_Java

架构 微服务 Feign springcloudAlibaba 12月日更

模块5作业

忘记喝水的猫

架构训练营

Linux里的“宝塔”,真正的宝塔!详细教程

老表

Linux 开发工具 安装宝塔 跟老表学云服务器

Git进阶(三):webstorm 的 git 切换分支

No Silver Bullet

git 12月日更

迈向云原生:名企FreeWheel应用架构演进

博文视点Broadview

SpringCloudAliBaba 组件之 Ribbon精讲【负载均衡】

XiaoLin_Java

负载均衡 微服务 Ribbon springcloudAlibaba 签约计划第二季

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