今天羣裏有個小夥伴問了個問題。

對某個欄位 sort 後,再 skip 一個超大值,之後 MongoDB 會報錯,因為用於排序的內存超過了 32m。如果 skip 一個小值,就不會報錯。

問題是,這個排序欄位是沒有索引的,那麼 sort 時數據都是放在內存裡面 sort。為啥 skip 小就沒事,skip 大就會撐爆內存呢?

小夥伴說,已經問了 10 個羣了,沒人回答為啥。

嗯?

skip 一個超大值,然後佔用很多內存報錯,這不是顯而易見的嗎。

幾乎每本資料庫的書都會提到,不要 skip 一個超大值,會產生性能問題,然後給出幾個替代方案。

仔細一想,有古怪。由於沒有索引,數據拿到內存裡面進行排序,那 skip 多和少應該沒有影響。但是事實上,skip 超大值的確會導致內存超出限制。

Google 了半天,全是怎麼處理這個問題,沒有提到為什麼產生這個現象。

經過謹慎的思(nao)考(bu),我提出了一個想法:

  1. 問題的根源顯然出現在 MongoDB 的排序演算法上,但是本人未能找到其演算法實現
  2. 如果我來實現排序演算法,我會將所有數據全部讀取後,再進行排序嗎?NO
  3. 在一個存在 limit、skip 的查詢語句中,資料庫排序後需要臨時保存的數據量是 limit + skip 條數據。
  4. 那資料庫的排序操作,很可能是先獲取 limit+skip 條數據作為一個 chunk,排序後,開始逐一獲取剩餘數據。之後每條新數據與 chunk 內數據進行比較,符合要求則進入,並踢出 chunk 內對應數據。
  5. 所以,當 skip 超大時,chunk 的數據量也超大,最後超過了 32m 從而報錯。

以上想法能夠解釋問題現象,但是沒有文檔依據。

好吧,那我們去看看官方文檔:

Add an index, or specify a smaller limit

In MongoDB, sort operations can obtain the sort order by retrieving documents based on the ordering in an index. If the query planner cannot obtain the sort order from an index, it will sort the results in memory. Sort operations that use an index often have better performance than those that do not use an index. In addition, sort operations that do not use an index will abort when they use 32 megabytes of memory.翻譯過來說,就是在mongodb中,sort操作能通過索引獲得在要排序的documents中的順序,如果執行計劃中沒有從索引中獲取順序,它就會在內存中對檢索結果進行排序。 使用索引的Sort操作在性能上往往比沒有使用索引的sort要好。另外。沒有使用索引的sort操作在使用了32M內存的時候會被終止掉(使用內存排序時,限制查詢結果默認最多為32M,超過的話查詢則會被終止)。關於mongoDB使用sort排序引發的生產bug

對問題的建議是添加索引,或者減少 limit,描述中限制查詢結果最多為 32m。

以上文檔能在一定程度上佐證我的想法。

推薦閱讀:

相關文章