连环触发!MongoDB核心集群雪崩故障背后竟是……( 五 )


继续分析MongoDB内核源码 , 发现使用随机数的地方很多 , 其中有部分随机数通过用户态算法生成 , 因此我们可以采用同样方法 , 在用户态生成随机数 , 用户态随机数生成核心算法如下:
class PseudoRandom {
... ...
uint32_t _x;
uint32_t _y;
uint32_t _z;
uint32_t _w;
}
该算法可以保证产生的数据随机分布 , 该算法原理详见:
也可以查看如下git地址获取算法实现:MongoDB随机数生成算法注释
总结:通过优化sasl认证的随机数生成算法为用户态算法后 , CPU sy% 100%的问题得以解决 , 同时代理性能在短链接场景下有了数倍/数十倍的性能提升 。
三、问题总结及疑问解答
从上面的分析可以看出 , 该故障由多种因素连环触发引起 , 包括客户端配置使用不当、MongoDB服务端内核极端情况异常缺陷、监控不全等 。 总结如下:

  1. 客户端配置不统一 , 同一个集群多个业务接口配置千奇百怪 , 超时配置、链接配置各不相同 , 增加了抓包排查故障的难度 , 超时时间设置太小容易引起反复重连 。
  2. 客户端需要配全所有mongos代理 , 这样当一个代理故障的时候 , 客户端SDK默认会剔除该故障代理节点 , 从而可以保证业务影响最小 , 就不会存在单点问题 。
  3. 同一集群多个业务接口应该使用同一配置中心统一配置 , 避免配置不统一 。
  4. MongoDB内核的新连接随机算法存在严重缺陷 , 在极端情况下引起严重性能抖动 , 甚至业务“雪崩” 。
分析到这里 , 我们可以回答第1章节的6个疑问点了 , 如下:
Q1:为什么突发流量业务会抖动?
答:由于业务是java业务 , 采用链接池方式链接mongos代理 , 当有突发流量的时候 , 链接池会增加链接数来提升访问MongoDB的性能 , 这时候客户端就会新增链接 , 由于客户端众多 , 造成可能瞬间会有大量新连接和mongos建链 。 链接建立成功后开始做sasl认证 , 由于认证的第一步需要生成随机数 , 就需要访问操作系统"/dev/urandom"文件 。 又因为mongos代理模型是默认一个链接一个线程 , 所以会造成瞬间多个线程访问该文件 , 进而引起内核态sy%负载过高 。
Q2:为何mongos代理引起“雪崩” , 流量为何跌零不可用?
答:原因客户端某一时刻可能因为流量突然有增加 , 链接池中链接数不够用 , 于是增加和mongos代理的链接 , 由于是老集群 , 代理还是默认的一个链接一个线程模型 , 这样瞬间就会有大量链接 , 每个链接建立成功后 , 就开始sasl认证 , 认证的第一步服务端需要产生随机数 , mongos服务端通过读取"/dev/urandom"获取随机数 , 由于多个线程同时读取该文件触发内核态spinlock锁CPU sy% 100%问题 。 由于sy%系统负载过高 , 由于客户端超时时间设置过小 , 进一步引起客户端访问超时 , 超时后重连 , 重连后又进入sasl认证 , 又加剧了读取"/dev/urandom"文件 , 如此反复循环持续 。
此外 , 第一次业务抖动后 , 服务端扩容了8个mongos代理 , 但是客户端没有修改 , 造成B机房业务配置的2个代理在同一台服务器 , 无法利用mongo java sdk的自动剔除负载高节点这一策略 , 所以最终造成”雪崩” 。
Q3:为什么数据节点没有任何慢日志 , 但是代理负载却CPU sy% 100%?
答:由于客户端java程序直接访问的是mongos代理 , 所以大量链接只发生在客户端和mongos之间 , 同时由于客户端超时时间设置太短(有接口设置位几十ms , 有的接口设置位一百多ms , 有的接口设置位500ms) , 就造成在流量峰值的时候引起连锁反应(突发流量系统负载高引起客户端快速超时 , 超时后快速重连 , 进一步引起超时 , 无限死循环) 。 Mongos和mongod之间也是链接池模型 , 但是mongos作为客户端访问mongod存储节点的超时很长 , 默认都是秒级别 , 所以不会引起反复超时建链断链 。
Q4:为何A机房代理抖动的时候 , A机房业务切到B机房后 , 还是抖动?
答:当A机房业务抖动 , 业务切换到B机房的时候 , 客户端需要重新和服务端建立链接认证 , 又会触发大量反复建链断链和读取随机数"/dev/urandom"的流程 , 所以最终造成机房多活失败 。
Q5:为何异常时候抓包分析 , 客户端频繁建链断链 , 并且同一个链接建链到断链间隔很短?
答:频繁建链断链的根本原因是系统sy%负载高 , 客户端极短时间内建立链接后又端口的原因是客户端配置超时时间太短 。
Q6:理论上代理就是七层转发 , 消耗资源更少 , 相比mongod存储应该更快 , 为何mongod存储节点无任何抖动 , mongos代理却有严重抖动?