分片,惟一索引和upsert
,表面上看似沒有直接聯繫的幾個東西,到底存在怎樣的瓜葛呢?mongodb
爲了保持水平擴展的有效性,分片功能必須保證各個片之間沒有直接關聯,不須要與其餘分片交互就能夠獨立作出決策。若是不能知足這一點,隨着分片數量不斷增長,須要交互的分片愈來愈多,勢必會愈來愈慢,那麼就違背了分片的初衷了。好比JOIN
就是一種典型的破壞分片獨立性的功能。在一個n
個分片的集羣中,爲了獲得笛卡爾積,每一個分片必須與其餘n-1
個分片交互來獲得結果。雖然不見得是線性的延遲增加(由於n-1
個請求能夠並行),可是可想而知對資源將是極大的消耗,而且隨着分片數量的增加影響會愈來愈顯著,最終會到達「增長一個分片可能對性能徹底沒有幫助」,或者「增長一個分片反而下降性能」的地步。架構
惟一索引是另一個顯著破壞分片獨立性的特性。前面對JOIN
的分析徹底適用於惟一索引,而且更糟的狀況是惟一索引還有有更進一步的惡劣影響,那就是在寫入數據的時候必須佔用一個跨分片的全局鎖,不然沒法保證其惟一性,可想而知對性能有怎樣的影響。這也是MongoDB爲何不打算去實現全局惟一索引的緣由。併發
有一種特殊狀況卻能夠改變這種不利情況,那就是惟一索引的鍵正好是片鍵的時候。片鍵一旦肯定,文檔該去哪一個分片就肯定了,那麼只要保證該鍵在這一個片上惟一就能夠了,再也不須要去與其餘分片協商。性能
從語義上講,咱們使用upsert
通常是但願一個鍵只出現一次的(否則每次insert
就行了)。這一點偏偏是惟一索引要乾的事情,而惟一索引又存在上面的所說的問題,所以惟一有意義的狀況則是upsert
使用的條件正好是片鍵,且片鍵惟一。
知足了上面這些條件就高枕無憂了嗎?並非。在決定一個鍵是否是存在,到執行update
/insert
之間,是存在空隙的。即,檢測和執行並不在一個原子操做中,也不可能在一個原子操做中,不然將是一個很大粒度的鎖。再說,MongoDB對文檔級別並無真正經過加鎖來控制,而是經過「樂觀併發控制」(optimistic concurrency control)來進行的。
所以,出於效率考慮,不是原子操做是正確的選擇,而解決這個問題也不是特別麻煩的事情,實際上只須要在遇到duplicate key異常的時候重試該操做就能夠了,由於重試的時候理論上就應該變成update
而再也不是insert
,天然避免了問題。或者,在4.2中直接實現了這類錯誤的自動重試(SERVER-37124)。架構設計
張耀星,MongoDB亞太區首席技術諮詢服務顧問。在MongoDB的開發、應用和諮詢服務上有多年實踐經驗。做爲MongoDB認證專家,曾經爲不一樣行業的各種大型客戶提供過培訓、性能調優、架構設計等各種MongoDB相關技術服務。設計