mongodb分片优化首次查询慢


现状

行业:iot,保存的是设备的历史数据。

数据库结构:一个库,按天分表,文档结构很简单,几个字段,其中id字段自定义。

数据量:每天的数据量在百万至千万之间,从正式上线到现在总共有二百多张表,数据量很大,索引大小就有50g左右。

索引:2个,除了默认的id索引,还有一个查询字段创建的索引,查询条件目前只有这个查询字段的=和in。

mongodb环境:版本4.2.3,分片加副本集搭建的集群,总共3个分片,每个分片一主一从。

问题

虽然mongodb使用了分片集群,但是一开始并没有对数据库和表进行分片(当然这是个失误),所有的数据都只往主分片上写,随着数据量的不断增加,主分片开始出现了性能瓶颈,导致了首次查询变慢,单表几百万的数据查询几百条,查询速度就已经超过了2s,而且是在命中索引的情况下,第二次会很快,因为这个时候已经查内存了。

优化

  1. 索引:刚开始并没有往分片的性能瓶颈上考虑,以为是索引建的有问题,经过反复测试得出,查询条件就那么一个,索引也是有效的,而且查出的结果也就几个字段,索引应该是没问题的。
  2. 预热:如果查询的数据并不在内存中,那么第一次查询肯定读的是磁盘,又以为是读磁盘慢,所以考虑是不是要先预热,很遗憾,并没有找到什么可行的方法(除了预查询),再者从别人的实际使用中得知,即使不预热,也不会出现首次查询慢,这种方式可以先排除。
  3. 内存:通过增加内存发现并没什么用,后来一想,内存增加只是为了保证数据和索引一直存在内存中,而不用和磁盘频繁交换,并不能解决第一次查询的性能问题。
  4. 分片:由于是第一次使用mongodb,经验不足,前面的排查导致走了不少弯路,后来经过查找资料发现不分片的表都全部往主分片写数据,使用db.stats()查看,果然所有的数据都在一个分片上,一看数据大小和索引大小发现,嚯,居然那么大了,最终定位在了单个分片的性能瓶颈上(分片的上限是多少不得而知,只是觉得这个可以作为一个优化的方向)。

分片

  1. 数据库启用分片
db.runCommand({enablesharding:"db_name"})
  1. 对已有数据的集合分片

最开始的方案:直接对原表开启分片

sh.shardCollection("db_name.collection_name", {_id:1})

表分片开启后,均衡器会根据分片规则把表的数据拆分并迁移到其他分片,经过测试发现,虽然表的数据拆分了,但是原分片的索引大小并没有减少反而增加,这样针对原分片的查询一样慢,为什么会这样,暂时还不知道原因,只能放弃这种方案。

现在的方案:创建新库新表,先启用分片,然后通过mongodump和mongorestore备份恢复数据到新表,步骤如下:

1. sh.shardCollection("new_db_name.collection_name", {_id: "hashed"})

2. mongodump -d old_db_name -c collection_name

3. mongorestore -d new_db_name -c  collection_name dump/old_db_name/collection_name.bson

分片结果:首次查询效率由原来的几秒减少到几百毫秒。
原来:

现在:

分片总结

  1. 原表数量多,每张表都需要执行上述命令,需要花一定时间。
  2. 为什么使用id哈希分片,而不用查询字段分片,目的是为了使数据在各个分片更平衡,弊端是查询的时候要跨多个分片,但测试发现并不会影响查询效率。
  3. 哈希索引所占空间比范围索引大。
  4. 如果分片片键指定的是除id外的字段,并且id是自定义的话,后端使用java的mongoTemplate的save会报错。
  5. 分片过程中如果出现以下问题:
Could not find host matching read preference { mode: "primary" } for set shard2
Connection closed by peer
Connection reset by peer

可能是mongo服务器内存不够,mongo服务重启导致的。

写在最后

第一次写博,这次mongodb的优化只是个人的一次经历,如果有什么不足之处或者不对的地方,还请各位提出指正,谢谢!


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM