1. 程式人生 > >如何對10億資料量級的mongoDB作高效的全表掃描

如何對10億資料量級的mongoDB作高效的全表掃描

本文連結: http://quentinXXZ.iteye.com/blog/2149440

一、正常情況下,不應該有這種需求

首先,大家應該有個概念,標題中的這個問題,在大多情況下是一個偽命題,不應該被提出來。要知道,對於一般較大資料量的資料庫,全表查詢,這種操作一般情況下是不應該出現的,在做正常查詢的時候,如果是範圍查詢,你至少應該要加上limit

說一下,我的應用場景:用於全量建立搜尋引擎的索引。這就是一種需要用到全表掃描的非一般情況。對於全表掃描的結果,我們沒有排序要求。

二、情況說明

既然有如此大的資料量,那儲存所佔空間基本都是上T的了。所以肯定是使用了mongodb叢集,而且配置了分片的分散式環境。

公司的伺服器,效能比較好,應該是24核,96g記憶體的。所以讀者們使用不同機器,測出來的用時,跟我這的結果可能不一定相符。

三、第一種方法,利用chunk資訊作劃分。

原理:我們知道,在分片環境下,mongodb使用chunk塊來組織資料。同一個chunk塊的資料肯定存在於同一個分片上。假設,我們以 _id”作分片時所用的片鍵。Mongodb為了保證範圍查詢的效率,一定會將一定範圍內的_id值的document方在同一個chunk中。而且通過mongodb自身提供的方法,我們可以很方便的獲取,每一個chunk的中maxKeyminKey[minKey,maxKey) 這個範圍內的資料肯定在同一個

chunk內,也並定在同一個分片中。

做法:1、先獲取所有chunk資訊,得到他們的maxKeyminKey

2、多執行緒執行,將這些chunk資訊,分發給這些執行執行緒。

3、各執行緒,根據當前的chunk maxKeyminKey資訊,做範圍查詢。

4、最後將結果彙總。

這樣做的好處在於:

1、每次對一個chunk做的範圍查詢,肯定是隻在一個分片(意味著同一塊硬碟)中進行的,不會分散到多個分片。這樣很高效。

2、可以方便的利用多執行緒,提高效率。

這種方法我沒試過,公司的前輩嘗試過,據說,最終用時3小時以內。是最為理想的效果。

四、改用雜湊片鍵後的全表掃描方法

用上面的方法,有一個前題,就是分片策略採用的是

mongodb預設的升序片鍵的方法。這樣才保證,升序的_id,會按排序範圍分佈在chunk塊中。這樣這策略,存在一個明顯的問題,就是會造成所以新增的doucument的寫入肯定都會命中到具有當前最大_idchunk上。造成寫入的分發的不平衡。

前文中,說過,全表掃描,應該是一個正常情況下不被允許的情況。所以資料庫策略的制定也不應該以考慮全表掃描的效率為優先,當前情況下,就應以寫入效為優先考慮。公司正在使用的片鍵策略,是片鍵策略雜湊片鍵(hashed shard key,這樣的話,寫入請況會被很好地分發到多個分片上,但是不利用進行範圍查詢。上面用的全表掃描方法就沒法再用了。

做法:1、獲得全域性的最大id maxID與全域性的最小id minID

      2、設定一個stepSize ,比如5000。將[minID,maxID]5000為一個chunk(我們定義的一個邏輯chunk)作切分,那麼第一個塊範圍[minID,minID+5000,

 3、各執行緒,根據分配到的chunk塊的maxIDminID資訊,做範圍查詢。

4、最後將結果彙總。

這樣做法的問題:

               1、對一個chunk進行查詢,會命中多個分片進行查詢,查詢效率大幅降低。

               2、如果_id分佈稀疏,查詢變得更快。因為以5000stepSize, 其中可能有不少_id是不存在的。測試同學幫我搭線下測試資料時,2000w條資料,由於計算錯誤,_id的範圍分佈劇然從10億起,到130億止。導致的執行緒幾乎一起在空跑chunk。所以_id分佈稀疏的情況下,這種查詢方式完全不適用。

               3、_id分佈不均。可能某個chunk中幾乎5000個滿載,有些chunk只有很少幾個_id有效。那麼就會導致計算資源分佈不均。

最後的結果不理想,我一開始,由於涉及一些聯表join操作,用時16多個小時。後來各種調整,加執行緒,去掉一些操作,差不多仍需10小時。

五、改用雜湊片鍵後的較高效全表掃描方法

上面的那種方法,當然是不理想的。光查資料就需要16個小時,加上後續處理,肯定更久,這樣我們的搜尋引擎索引建立,就不可能當前完成了。但一直苦於沒有更好的方法。

最終這個問題,還是被我的頭解決了。 先說一下,最終效果吧。20w/秒,約3小時完成。

其實,最終的方法很簡單,但必須放棄多執行緒(多cursor)。

做法:使用單執行緒掃描,不加適合可能影響排序的條件。

這樣做的目的是使用mongodb中的自然排序。掃描時,必然是依次命中某一個分片讀取,不會帶來磁碟震盪問題。