线上某核心 mongodb 集群数据量不大,单表数据量十亿级,但是该集群比较核心,影响公司收入流水。本文通过分享本次踩坑来分享整个故障经过,该故障为一次经典的 mongodb 分片 sharding 集群踩坑故障,包括变更通知不到位、部署架构不到位、变更考虑不仔细等。
前滴滴出行技术专家,现任 OPPO 文档数据库 mongodb 负责人,负责 oppo 千万级峰值 TPS/十万亿级数据量文档数据库 mongodb 内核研发及运维工作,一直专注于分布式缓存、高性能服务端、数据库、中间件等相关研发。Github 账号地址:https://github.com/y123456yz
本文是《mongodb 源码实现、调优、最佳实践系列》专栏的第 18 篇文章,其他文章可以参考如下链接:
Qcon-万亿级数据库MongoDB集群性能数十倍提升及机房多活容灾实践
Qcon现代数据架构-《万亿级数据库MongoDB集群性能数十倍提升优化实践》核心17问详细解答
百万级高并发mongodb集群性能数十倍提升优化实践(上篇)
百万级高并发mongodb集群性能数十倍提升优化实践(下篇)
盘点2020 |我要为分布式数据库mongodb在国内影响力提升及推广做点事
话题讨论| mongodb拥有十大核心优势,为何国内知名度远不如mysql高?
mongodb详细表级操作及详细时延统计实现原理(快速定位表级时延抖动)
[图、文、码配合分析]-Mongodb write写(增、删、改)模块设计与实现
300条数据变更引发的血案-记某十亿级核心mongodb集群部分请求不可用故障踩坑记
某核心 mongodb 历史集群(入职前就有的一个集群),在对现在所有 mongodb 集群进行风险梳理过程中,发现该集群存在一些潜在的集群抖动风险,该集群架构及流量时延曲线如下:
流量曲线如下:
时延曲线如下:
如上图所示,该分片集群由 3 个分片组成,集群读写流量很低,峰值 QPS 约 4-6W/s,平均时延 1ms,每个分片采用 mongodb 复制集架构实现高可用。通过巡检发现该集群存在如下几个问题:
库表信息如下表所示:
上面的描述可以总结为下图:
从上图可以看出,分片 2 和分片 3 几乎没起到任何作用;由于分片 3 有两个节点为低 IO 的 sata 盘,可能影响 userbucket 库的读写,因此考虑直接 removeShard 从集群中剔除分片 3 和分片 2。
由于分片 3 为低 IO 服务器,有潜在抖动集群抖动分享;同时分片 2 和分片 3 几乎都是浪费的分片,因此打散直接通过如下 removeshad 命令删除分片 3 和分片 2 信息,腾出无用服务器资源,如下图所示:
登陆监控列表中的其他两个代理 mongos2、mongos3,通过如下命令强制刷新路由信息: db.adminCommand({"flushRouterConfig":1})
注意事项:
由于 movePrimary 过程,其他代理不会感知到该库的主分片变化,因此需要强制刷新路由信息或者重启其他节点的 mongos,参考如下:
对含有 300 条数据的 userbucket 库变更后,当我还在若无其事的处理其他集群性能调优的时候,用户突然很急的电话我反馈该核心集群整个访问不可用(注意:是整个 10 亿数据的集群不可用)。
收到电话后很突然,和业务人员详细对接后可以基本上确定是因为这 300 条数据变更引起。业务获取这 300 条数据的时候,部分请求获取成功,部分请求获取失败,说明肯定和 movePrimary 有关系。
于是,除了对监控列表中的所有代理做 flushRouterConfig 强制路由刷新外,还重启了所有的代理,但是业务反馈,还是有部分请求获取不到数据。比较棘手,我自己通过所有的 mongos 代理查看 userbucket 库下面的 300 条数据,完全可以获取到数据。
于是怀疑是不是还有未刷新路由的 mongos 代理,于是登陆任一 mongos 代理获取 config.mongos 表,查看结果如下:
上面的 config.mongos 表记录了该集群所有的代理信息,同时记录了这些代理和集群最后一次 ping 通信的详细时间信息。很明显,该表中记录的代理原不止集群监控列表中的代理个数,比监控列表中的个数要多。
最终,把 config.mongos 表中罗列的当前在线的所有代理强制通过 flushRouterConfig 刷新路由后,业务恢复。
通过前面的分析可以得出,由于早期集群监控中漏掉了部分代理,造成这部分代理对应的 userbucket 路由信息是 movePrimary 前的路由信息,也就是指向了错误的分片,因此出现了路由不到数据的情况,如下图所示:
因为部分代理在 moveprimary 后,没有强制刷新该表路由信息,造成部分代理路由获取数据的时候路由错误。
和业务实现逻辑有关系,因为业务在获取这 10 亿条数据前首先需要获取业务的路由信息,刚好业务路由信息存在了 userbucket 库对应表中,业务在获取数据前必须要获取到业务的路由信息数据,如果 userbucket 数据获取不到,用户就无法确定指向 feeds_xxxxxxx 数据。
历史原因,造成部分代理业务代码有配置,但是服务端集群监控元数据遗漏了,也就是服务端集群监控漏掉了部分代理,这部分代理没有监控起来。也有可能是 mongos 代理扩容,但是集群监控列表中没有加入元数据。
官方建议 movePrimary 操作成功后需要强制路由刷新或者重启 mongos,但是 movePrimary 操作成功和 mongos 重启这个过程中有个中间状态,如果中间状态业务读或者些该迁移的库下面的表,还是可能路由错误。因此,最佳安全的 moveprimary 可以通过如下两个方法操作:
法一:shutdown 所有代理,只留下一个代理,等该代理 moveprimary 成功后在重启其他 mongos 代理。切记别遗漏代理,出现本文踩坑类似情况,提前检查 config.mongos 表。
法二:对主分片在该需要 removeShard 的分片的库中的所有表启用分片功能,启用分片功能后会有 chunk 信息,mongodb 会自动迁移该分片的 chunk 到其他分片,整个过程可以保证路由信息一致。
https://github.com/y123456yz/reading-and-annotate-mongodb-3.6
领取专属 10元无门槛券
私享最新 技术干货