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


连环触发!MongoDB核心集群雪崩故障背后竟是……文章插图
从上图可以看出 , 客户端6000并发反复重连 , 服务端压力正常 , 所有CPU消耗在us% , sy%消耗很低 。 用户态CPU消耗3个CPU , 内核态CPU消耗几乎为0 , 这是我们期待的正常结果 , 因此觉得该问题可能和操作系统版本有问题 。
为了验证更高并反复建链断链在Linux-3.10内核版本是否有2.6版本同样的sy%内核态CPU消耗高的问题 , 因此把并发从6000提升到30000 , 验证结果如下:
测试结果:通过修改MongoDB内核版本故意让客户端超时反复建链断链 , 在linux-2.6版本中 , 1500以上的并发反复建链断链系统CPU sy% 100%的问题即可浮现 。 但是 , 在Linux-3.10版本中 , 并发到10000后 , sy%负载逐步增加 , 并发越高sy%负载越高 。
总结:linux-2.6系统中 , MongoDB只要每秒有几千的反复建链断链 , 系统sy%负载就会接近100% 。 Linux-3.10 , 并发20000反复建链断链的时候 , sy%负载可以达到30% , 随着客户端并发增加 , sy%负载也相应的增加 。 Linux-3.10版本相比2.6版本针对反复建链断链的场景有很大的性能改善 , 但是不能解决根本问题 。
4、客户端反复建链断链引起sy% 100%根因
为了分析%sy系统负载高的原因 , 安装perf获取系统top信息 , 发现所有CPU消耗在如下接口:
连环触发!MongoDB核心集群雪崩故障背后竟是……文章插图
从perf分析可以看出 , cpu 消耗在_spin_lock_irqsave函数 , 继续分析内核态调用栈 , 得到如下堆栈信息:
- 89.81% 89.81% [kernel] [k] _spin_lock_irqsave - _spin_lock_irqsave
- mix_pool_bytes_extract
- extract_buf
extract_entropy_user
urandom_read
vfs_read
sys_read
system_call_fastpath
0xe82d
上面的堆栈信息说明 , MongoDB在读取 /dev/urandom, 并且由于多个线程同时读取该文件 , 导致消耗在一把spinlock上 。
到这里问题进一步明朗了 , 故障root case 不是每秒几万的连接数导致sys 过高引起 。 根本原因是每个mongo客户端的新链接会导致MongoDB后端新建一个线程 , 该线程在某种情况下会调用urandom_read 去读取随机数/dev/urandom, 并且由于多个线程同时读取 , 导致内核态消耗在一把spinlock锁上 , 出现cpu 高的现象 。
5、MongoDB内核随机数优化
1) MongoDB内核源码定位分析
上面的分析已经确定 , 问题根源是MongoDB内核多个线程读取/dev/urandom随机数引起 , 走读MongoDB内核代码 , 发现读取该文件的地方如下:
连环触发!MongoDB核心集群雪崩故障背后竟是……文章插图
上面是生成随机数的核心代码 , 每次获取随机数都会读取”/dev/urandom”系统文件 , 所以只要找到使用该接口的地方即可即可分析出问题 。
继续走读代码 , 发现主要在如下地方:
//服务端收到客户端sasl认证的第一个报文后的处理 , 这里会生成随机数
//如果是mongos , 这里就是接收客户端sasl认证的第一个报文的处理流程
Sasl_scramsha1_server_conversation::_firstStep(...) {... ...
unique_ptr sr(SecureRandom::create);binaryNonce[0] = sr->nextInt64;
binaryNonce[1] = sr->nextInt64;
binaryNonce[2] = sr->nextInt64;
... ...
}
//mongos相比mongod存储节点就是客户端 , mongos作为客户端也需要生成随机数
SaslSCRAMSHA1ClientConversation::_firstStep(...) {... ...
unique_ptr sr(SecureRandom::create);binaryNonce[0] = sr->nextInt64;
binaryNonce[1] = sr->nextInt64;


推荐阅读