郭德纲经常调侃于谦,说于谦的三大爱好是抽烟、喝酒、烫头,用了这么多年 MongoDB 碰到太多问题,即使以前和现在都不是我在运维,总结下来对百万日活级别的 MongoDB 日常使用的大部分问题都可以归纳到以下几点。
磁盘不行
早期创业时我们用的 MongoDB 2.4,那个时候 MongoDB 出来没多久,当时的团队很早就在国内生产环境大规模上了 MongoDB,也是为了省钱 MongoDB 节点全是机械磁盘加 raid 5,硬是扛了 100 多万的日活,后期出问题最多就是磁盘,大并发写的时候磁盘 IO 吃紧无法满足性能,有时候莫名其妙卡顿,加缓存上内存优化代码各种招都试了,等到我们拿到融资把机器全换 SSD 之后,问题一下全没了。
有些钱省不得,省了之后总是会带来更大的麻烦,如果你生成环境用 MongoDB 建议直接 SSD,再说现在都云了,非常容易更换,不像我们当年,扛着磁盘去机房换,累死累活。
内存不够
MongoDB 为了性能几乎把所有热点数据都加载到内存了,很吃内存,早期我们对 MongoDB 不了解,机器都是自建的同样也是为了省钱机器配置都是 8CPU x 16G 的,用户量大时候莫名其妙 MongoDB 进程被操作系统 kill 掉,回顾日志发现大部分原因是 MongoDB 吃了太多内存。后来我们升级到 64G 之后再也没有出现过类似的问题。
MongoDB 的非常吃内存,如果生产环境大量使用 MongoDB 建议对重点业务机器配置高内存。
索引使用不合理
同 MySQL 一样,如果 MongoDB 的索引使用不合理,会导致大量的慢查询,从而过快消耗完数据库连接,服务器 CPU load 非常高,数据库响应缓慢。MongoDB 的索引的最佳实践官方说的非常透彻,建议好好学习,认真实践:https://docs.mongodb.com/manual/indexes/ 。
除此之外,我也写过一篇关于 MongoDB 索引的文章:Mongodb 中的索引 读者可参考借鉴。
什么时候使用索引?
最好索引在建表之初就设计好,不要等到数据量大到一定程度把问题暴露出来了再去创建索引,那个时候出问题可能就是个大问题了。
行记录太大
在一次定位线上应用程序莫名卡顿时,发现了 MongoDB 中一个慢查询,最后诊断下来发现由于开发人员的粗心大意,一条 MongoDB 的 record 竟然达 10m 左右,导致 MongoDB client 在查询和传输数据时都非常的慢。
MongoDB 单条数据最大不超过 16m,如果再大官方建议使用 gridfs 或者其他文件存储系统,显然 MongoDB 有自己不擅长的地方,在使用时要注意。
collection 数据量太大
MongoDB 单节点单表数据达数亿级别时在大并发写入时会变慢,此时可以考虑拆表,如果业务数据量不是很大,可以直接在原有集群上进行。MongoDB 官方提供了 sharding 方案,但是对于一般性业务来说显得过于复杂,可以根据自身的业务特性选择合适的方案实践。
日常 MongoDB 的诊断
https://docs.mongodb.com/manual/tutorial/manage-the-database-profiler/
MongoDB 提供了慢查询日志和错误日志来帮助定位和解决问题,慢查询日志可以通过 db.setProfilingLevel
设置,共有3个级别分别是:
- 0 默认级别,不收集任何数据
- 1 收集满足条件的慢查询
- 2 所有操作都收集
开启 slow log db.setProfilingLevel(1, { slowms: 20 })
,查询慢查询日志 db.system.profile.find( { millis : { $gt : 5 } } ).pretty()
除了慢查询日志,db.serverStatus
可以查看当前服务器各种状态,包括连接数、锁信息、文档更新删除数量等等能够帮助我们迅速判断服务器的状态。