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