MongoDB Sorted Operation OOM

Posted by Alan Zhan on Sunday, March 20, 2022

這篇應該會是難得的從問題反推回去學習的歷程吧,我得好好記錄紀錄。最近我們的 production 發生了 MongoDB 的 sort 操作的限制,具體訊息如下:

MongoDB.Driver.MongoCommandException: Command find failed: Encountered non-retryable error during query :: caused by :: Executor error during find command :: caused by :: Sort operation used more than the maximum 33554432 bytes of RAM.

好吧!問題都發生了,我們先來解決問題,所以我們就先加了 index 上去應急,至於甚麼是 mongoDB 的 index 呢?

什麼是 MongoDB Index 呢?

如果你剛好是 DB 的設計者,你會怎麼找資料返回你的 Client 呢?

這個問題不是很簡單嘛!我就一條一條一條資料過濾,然後再一口氣一起排序,再把你需要的資料返回給你。這個在資料量很小的狀態下,在使用的體驗上是不會有任何感覺的,會覺得資料很快速就拿到了,然而只要資料量一大起來,這樣的查找資料的方式,是不是很低效呢?

在一般正常狀況下,如果你的資料量越大,那麼你的資料量與查詢效率是呈正比的,所以這時候我們就需要 index 來幫助我們解決資料查詢會造成的查詢效率低落的問題,那甚麼是 index 呢?

Index 是用來解決查詢效率低落的問題,同時它也是把雙面刃,在添增 index 的同時,它會有額外的開銷,所以在使用上我們還是要理性的添加 index :

  • Pros
    1. 加速查詢效率。
    2. 可利用 index 的唯一性來控制資料重複的問題。
    3. 使用 index 可以在排序時,加速的排序時間與減少記憶體花費。
  • Cons
    1. Index 會讓資料寫入時的速度降低。
    2. Index 會需要額外的空間儲存。

了解完畢 index 後,我們來看看官方對於 index 怎麼說, MongoDB Manaul Index

mongodb index btree

原來你是使用 Btree 來設計的啊,簡單來說 Btree 時間複雜度是不固定的,與 key 在樹中的哪個位置有關,但最好的情況是 O(1) 。那 MongoDB 有哪些 Index 種類呢?

MongoDB Index 種類

  • Unique Index : 唯一 index ,可以確保某個 field 的唯一性。在默認的情況下, MongoDB 在創建 collection 的時候,為 _id 欄位增加的唯一 index 。
  • Single Field Index : 單一欄位的 index 。
  • Compound Index : 多個欄位的 index 。在日常中,我們比較常會使用這個類型,來建立索引,因為你在查找資料的過程,不太可能只有下一個條件。
  • Multikey Index : 有點類似於 single filed index ,不過差別是在 array 的 field 或者是在 array 內的 object 上的 field 建立 index。
  • Text Index : 有點類似於 Elasticsearch 上的檢索功能,不過分詞功能沒有想是 Elasticsearch 那麼完善。
  • etc

這邊就先總結比較常用的,當然 MongoDB 也是有推出像是 geo 等等方便的 index,細節可以上官方文件觀看, MongoDB Manaul Index

OOM Sort Operation

稍微了解一下 index 之後,那麼為什麼會發生 OOM Sort 呢?

我們試著想一下,如果你把所有的資料從 disk 讀取到記憶體中做排序,那麼會發生甚麼是呢?所以 MongoDB 官方會做出這種決定是很正常的。

那我們總得要解決問題吧?我們又該如何解決呢?

增加排序可用的記憶體空間

我們可以增加可用的記憶體空間,這邊的範例是改成 100 MB。

db.adminCommand({ setParameter: 1, internalQueryExecMaxBlockingSortBytes: 104857600 })

使用 Disk 當作記憶體

如果你使用 aggregation pipeline 的話,你可以使用 allowDiskUse: true 來突破記憶體限制,讓 disk 來幫助你!

新增 index

上面兩個方法可以幫你快速解決問題,不過建議還是會使用 index 來解決,記得把 background 設置為 true,不然你在 production 建立 index 的時候,因為資料量過多造成寫入鎖死。

db.Foo.createIndex({ "Bar": 1 }, { background: true })

總結

至於 index 要怎麼設計規劃呢?讓我賣賣關子,下幾篇會再給大家好好的解釋清楚。

歡迎到我的 Facebook Alan 的筆記本 留言,順手給我個讚吧!你的讚將成為我持續更新的動力,感謝你的閱讀,讓我們一起學習成為更好的自己。

參考