mongoDB shardKey

譯註:本文探討了如何合理設置MongoDB片鍵以發揮分片機制的優勢,作者爲Bugsnag.com的工程師Conrad Irwin。Bugsnag爲移動應用開發者提供實時的Bug追蹤及檢測服務,Bugsnag使用MongoDB存儲超過TB級的文檔數據。轉載請註明來自kevinsj.com

原文鏈接

簡而言之,使用 {_id: 'hashed'}  {projectId: 1, _id: 1} 來作爲片鍵。

幾個月前,我們對MongoDB集羣進行分片(shard)處理,數據設置了兩個副本集合(replica set)。上週,我們添加了一個新的分片。首次分片花了一些功夫,不過我們仍然在沒有停機的情況下完成了這個工作,如今添加一個新的分片很輕而易舉。

1. MongoDB的分片是如何工作的?

MongoDB的分片機制能夠幫助你將你的數據庫劃分到多個服務器,通常在生產環境中可以將數據集劃分到多個副本集中。但分片最好在數據庫建立早期劃分,因爲一旦你的數據大於512GB那麼分片劃分就不是那麼容易了。這受到MongoDB縱向擴展能力的限制。

爲了實現分片,你必須向MongoDB指定使用哪個索引作爲片鍵,然後MongoDB會根據你的設置將你的數據劃分到有着相同片鍵的數據塊(Chunk)中。而後這些數據塊將根據片鍵的大致順序分散到副本集中。

sharding

正如你所見,分片之後數據的存放位置依賴於片鍵,所以合理的選擇片鍵十分重要。

2. 好片鍵的要素

MongoDB的內部機制保證了每個副本集(RS)包含了同樣數量的塊,在上圖中一個RS包含兩個塊,而在 Bugsnag.com 的集羣中,每個RS包含6300個塊。但這幾乎是唯一的保證機制了。

片鍵的選擇決定了三個重要的方面:

1. 1. 讀和寫的分佈

其中最重要的一點是讀和寫的分佈。如果你總是朝一臺機器寫,那麼這臺機器將會成爲寫瓶頸,則你的集羣的寫性能將會降低。這無關乎你的集羣有多少個節點,因爲所有的寫操作都只在一個地方進行。因此,你不應該使用單調遞增的 _id 或時間戳作爲片鍵,這樣將會導致你一直往最後一個副本集中添加數據。

相類似的是如果你的讀操作一直都在同一個副本集上,那麼你最好祈求你的任務能在機器內存所能承受的範圍之內。通過副本集將讀請求劃分開能夠使你的工作數據集大小隨着分片數線性擴展。這樣的話你能夠將負載壓力均分到各臺機器的內存和磁盤之上。

2. 2. 數據塊的大小

其次是數據塊的大小。MongoDB能夠將大的數據塊劃分成更小的,但這種情況僅僅在片鍵不同的情況下發生。如果你有巨量的數據文檔都使用了同樣的片鍵,那麼你相應的會得到巨大的數據塊。出現巨大塊是非常不好的,不僅僅因爲它會導致數據的不平均分佈,還因爲一旦這個數據塊的大小超過某個值,那麼你就不能夠在分片之間移動它了。

3. 3. 每個查詢命中的分片數目

最後一點,如果能夠保證大部分的查詢請求都能夠命中儘可能少的分片那就最好了。對於一個查詢請求來說,其延遲直接取決於最慢的那個命中服務器的延遲;所以你命中的分片越少,那麼理論上來說查詢將會越快。這一點並不是硬性的規定,不過如果能夠做到充分考慮那麼應該是很有利的。因爲數據塊在分片上的分佈僅僅是近似的遵循片鍵的順序,而並不是嚴格的強制指定。

3. 好片鍵是如何煉成的?

上面說了這麼多,那麼怎麼才能設計一個好的片鍵呢?

1. Hashed id

作爲第一個方案,你可以使用數據文檔 _id 的哈希作爲片鍵。

Python

db.events.createIndex({_id: 'hashed'})

這個方案能夠是的讀和寫都能夠平均分佈,並且它能夠保證每個文檔都有不同的片鍵所以數據塊能夠很精細。

似乎還是不夠完美,因爲這樣的話對多個文檔的查詢必將命中所有的分片。雖說如此,這也是一種比較好的方案了。

2. Multi-tenant compound index

如果想擊敗哈希索引模式,那麼你需要將關聯的文檔在索引中儘可能聚集在一起的方法。在Bugsnag,我們通過project聚合文檔,因爲在我們的業務場景中,我們的app大部分的查詢請求都在project範圍內。所以對於你的app來說你得指定適合你的聚合方式。

但是我們不能簡單地使用projectID作爲片鍵,因爲那會導致巨大塊的產生,所以我們引入了_id來將大project打散到多個塊中。這些打散的塊仍舊是索引連續的,所以仍然會分佈在用一個分片上。

Python

db.events.createIndex({projectId: 1, _id: 1})

這個方案很適合我們,因爲對於一個project來說,讀和寫幾乎是獨立於project存在時間的,並且舊的project通常都會被刪除掉。如果情況改變,我們可能會看到在新的project會有微小的負載上升情況。

爲了避免這種問題,我們未來可能會在當MongoDB支持哈希值的混合索引之後,將索引設置爲 {projectId: 'hashed', _id: 1} 。相關文檔(SERVER-10220)[https://jira.mongodb.org/browse/SERVER-10220]

3. 總結

找一個好的片鍵是很難的,不過這真的只有兩種方案。如果在應用中找不出一個好的聚合鍵,那麼對 _id 做哈希吧。如果你能夠找到,那麼將它與 _id 聚合以避免巨大塊的產生。請記住無論你使用何種聚合鍵,它都需要能夠將讀和寫平均分佈以充分利用集羣中的每個節點。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章