多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
## 问题 通常我们使用MongoDB的时候,客户端(driver)和MongoDB之间都是使用长连接,但是在某些场景下、某些driver仍然只能使用短连接进行连接,比如PHP。就在我们[阿里云数据库MongoDB版](https://www.aliyun.com/product/mongodb/?spm=5176.7960203.201.29.kLeoev)商业化后没多久,我们就遇到了一个用户短连接过多导致的性能问题。 这个问题的症状是MongoD的CPU使用率居高不下,16个核都跑满了,影响到了用户的正常使用。 ## 排查 首先想到的当然是看看有没有很多慢查询,针对存在的慢查询都建议用户建了索引后,情况还是没有好转。这时我们观察到用户的driver不断有短连接上来,基本上身份认证完执行个操作后很快就断开。会不会是短连接的问题导致呢?因为[阿里云数据库MongoDB版](https://www.aliyun.com/product/mongodb/?spm=5176.7960203.201.29.kLeoev)出于安全考虑,强制打开了身份认证,我们知道 MongoDB3.0 后使用的身份认证机制是[SCRAM-SHA1](https://yq.aliyun.com/articles/16919?spm=5176.group11.0.0.ZGL8ST),这是需要进行一些 CPU 密集型操作比如哈希计算等的。通过profile工具进行诊断后,我们发现 CPU 热点非常集中,主要消耗在读取 `/dev/urandom` 来生成随机数,这需要通过系统调用进入内核态。再结合监控,确实 CPU 都消耗在sys而不是user。以下为profile工具诊断图: ![](https://box.kancloud.cn/2016-04-22_5719d9fee30c4.jpg) 对照一下源码就知道了,这是因为在认证过程中需要生成一串叫做server-nonce的随机字符串(具体的认证过程及这个随机字符串的作用可参照[MongoDB中使用的SCRAM-SHA1认证机制](https://yq.aliyun.com/articles/16919?spm=5176.group11.0.0.ZGL8ST%EF%BC%89)。在Linux平台下,是通过这个`/dev/urandom`(Linux上提供了两个随机数生成的特殊设备文件,还有一个是`/dev/random`,这两个的区别是从`/dev/urandom` 中读取是非阻塞的,具体可再自行google或看man手册。)来生成随机数的。其原理是利用当前系统的熵池来计算出一定数量的随机比特,然后将这些比特作为字节流返回。熵池就是当前系统的环境噪音,因此其随机效果还是比较好的。不过有利必有弊,这个`/dev/urandom`的性能就不怎么好了。要使用这个来生成随机数,首先要进行系统调用,这涉及用户态到内核态的切换。其次,为了避免两个并发读返回同样的结果,在内核中使用了spinlock,因此在多线程并发从`/dev/urandom`中读时,性能会急剧下降。这个东东设计之初是为了安全,而不是性能。因此建议的做法是使用`/dev/urandom`来初始化用户态的随机数,而不是每个请求都从这里取。 ## 解决 既然知道了问题,那么解决也就好办了,当然是想办法使用一个性能较好,随机性又不差的方案来替换这个`/dev/urandom`。我们使用了一个用户态的随机数生成器,效果非常明显,CPU使用率从100%降到10%。 以下为优化前后CPU使用率对比图: ![](https://box.kancloud.cn/2016-04-22_5719d9ff3b2e3.jpg) 其中红色、蓝色、绿色分别为关闭Auth、打开Auth(优化前)和打开Auth(优化后)。 目前[阿里云数据库MongoDB版](https://www.aliyun.com/product/mongodb/?spm=5176.7960203.201.29.kLeoev)已经全面使用了性能优化版,所以各位使用短连接(如PHP driver)的客官们可以放心使用了。