前言
分片,唯一索引和upsert,表面上看似沒有直接聯系的幾個東西,到底存在怎樣的瓜葛呢?
分片
為了保持水平擴展的有效性,分片功能必須保證各個片之間沒有直接關聯,不需要與其他分片互動就可以獨立做出決策,如果不能滿足這一點,隨著分片數量不斷增加,需要互動的分片越來越多,勢必會越來越慢,那么就違背了分片的初衷了,比如JOIN就是一種典型的破壞分片獨立性的功能,在一個n個分片的集群中,為了得到笛卡爾積,每個分片必須與其他n-1個分片互動來得到結果,雖然不見得是線性的延遲增長(因為n-1個請求可以并行),但是可想而知對資源將是極大的消耗,并且隨著分片數量的增長影響會越來越顯著,最侄訓到達“增加一個分片可能對性能完全沒有幫助”,或者“增加一個分片反而降低性能”的地步,
唯一索引
唯一索引是另外一個顯著破壞分片獨立性的特性,前面對JOIN的分析完全適用于唯一索引,并且更糟的情況是唯一索引還有有更進一步的惡劣影響,那就是在寫入資料的時候必須占用一個跨分片的全域鎖,否則無法保證其唯一性,可想而知對性能有怎樣的影響,這也是MongoDB為什么不打算去實作全域唯一索引的原因,
有一種特殊情況卻可以改變這種不利狀況,那就是唯一索引的鍵正好是片鍵的時候,片鍵一旦確定,檔案該去哪個分片就確定了,那么只要保證該鍵在這一個片上唯一就可以了,不再需要去與其他分片協商,
upsert
從語意上講,我們使用upsert一般是希望一個鍵只出現一次的(不然每次insert就好了),這一點恰恰是唯一索引要干的事情,而唯一索引又存在上面的所說的問題,因此唯一有意義的情況則是upsert使用的條件正好是片鍵,且片鍵唯一,
滿足了上面這些條件就高枕無憂了嗎?并不是,在決定一個鍵是不是存在,到執行update/insert之間,是存在空隙的,即,檢測和執行并不在一個原子操作中,也不可能在一個原子操作中,否則將是一個很大粒度的鎖,再說,MongoDB對檔案級別并沒有真正通過加鎖來控制,而是通過“樂觀并發控制”(optimistic concurrency control)來進行的,
因此,出于效率考慮,不是原子操作是正確的選擇,而解決這個問題也不是特別麻煩的事情,實際上只需要在遇到duplicate key例外的時候重試該操作就可以了,因為重試的時候理論上就應該變成update而不再是insert,自然避免了問題,或者,在4.2中直接實作了這類錯誤的自動重試(SERVER-37124),
參考資料
- Unique Indexes: https://docs.mongochina.com/core/index-unique.html
- Retry full upsert path when duplicate key exception matches exact query predicate: https://jira.mongodb.org/browse/SERVER-37124
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/40065.html
標籤:NoSQL
