本系列文章翻譯自《50 Tips and Tricks for MongoDB Developers》,暫時沒有找到中文版,反正自己最近也在深入學習mongodb,所以正好拿來翻譯一下。一方面加強自己學習的效果,另一方面讓大家也一起來體驗一下需要我們這些mongodb使用者需要注意的地方。
首先聲明自己的英文水平不是太高,加之有些英文翻譯成中文也找不到合適的詞來表達,所以在文章中可能會出現英文原詞,或者說有些地方的翻譯會有些生硬,也就是說會出現直譯的地方。翻譯該書的主要目的是為大家學習探討用的,如果有翻譯不精準的地方,或者說有更加精準的翻譯,還請大家指出,我會及時的更正的,在此先謝過各位了。
Tip#1.Duplicate data for speed,reference data for integrity
資料冗餘是為了效能,引用資料是為了完整性。
被多個文檔使用的資料,既可以直接嵌入文檔,也可以在文檔中引用資料。嵌入不一定就比引用好,反之,引用也不一定就比嵌入好。每一種都有自己的取捨,不論什麼,你都應該選擇適合你的應用程式的方式。
嵌入式的結構,可能會導致資料的不一致。假設你需要把圖1.1中的fruit的值從蘋果修改為鴨梨,你剛修改完food集合中的fruit值,這時候你的應用崩潰了,其他地方的fruit的值還是舊的值,這時候你的應用中就同時存在兩種不同的fruit值。
650) this.width=650;" border="0" src="http://www.bkjia.com/uploads/allimg/131228/134335JI-0.png" alt="" />
圖1.1 嵌入式結構,fruit的值既存在於food集合,也存在於meals集合
不一致性也不是什麼大問題,但“不是大問題”也是有層級的,這個層級依賴於你的使用者需求。對於很多的應用,短時間內的不一致是可以接受的。假設一個使用者修改了他的姓名,在幾個小時內,他的舊文章繼續顯示他的舊姓名,是可以接受的。如果是即使短時間的不一致性也不能接受的話,你就需要考慮使用引用式的結構了。
650) this.width=650;" border="0" src="http://www.bkjia.com/uploads/allimg/131228/1343353C5-1.jpg" alt="" />
圖1.2 引用式結構,fruit的值只存在於food集合,meals集合儲存fruit的id
這需要權衡,你不能同時擁有最好的效能和確保及時的資料一致性。你必須決定哪一個對於你的應用來說更重要。
舉個例子來說。
假設我們正在設計一個購物車的應用,設計在mongodb中存在訂單資訊,訂單需要包含哪些資訊呢?
引用式結構
- a product:
- {
- "_id":productId,
- "name":name,
- "price":price
- }
- a order:
- {
- "_id":orderId,
- "user":userInfo,
- "items":[
- productId1,
- productId2
- ]
- }
在訂單的item項中存在每一個productid,當需要顯示訂單內容的時候,首先查詢order集合,然後根據productid查詢product集合來擷取相應的產品名稱,沒有辦法做到只用一次查詢就可以擷取完整的訂單資訊。
如果產品資訊被更新,所有引用該產品的地方,都會顯示新的產品資訊。引用式的結構會拖慢讀資料的速度,但是在多個訂單會有很好的一致性,多個文檔能實現原子的變化只需要修改引用的文檔的資訊)。
嵌入式結構
- a product:
- {
- "_id":productId,
- "name":name,
- "price":price
- }
- a order:
- {
- "_id":orderId,
- "user":userInfo,
- "items":[
- {
- "_id":productId1,
- "name":name,
- "price":price
- },
- {
- "_id":productId2,
- "name":name,
- "price":price
- }
- ]
- }
將產品資訊嵌入到訂單資訊中,當需要顯示訂單的時候,只需要執行一次查詢即可。如果產品的資訊發生變化,而且我們想要將變化傳遞給訂單的話,我們需要更新多個獨立的訂單。
嵌入式結構加快了讀取的速度,但是一致性會降低,產品資訊不能被原子的在多個文檔中被修改。
決定使用嵌入式結構還是使用引用式結構,可以參考下面的因素:
- 為很少發生變化的資料,每次都是再次讀取,你是否願意付出這樣的代價?
你是願意承擔1000次讀取帶來的懲罰?大多數應用,讀的壓力要大於寫的壓力。這需要你仔細測試自己的比例。
你正在考慮的引用資料的變化頻率如何?改變越少,越是贊成使用嵌入式的結構。引用很少改變的資料,例如名稱,出生日期,存貨標記和地址,是很不值的。
如果一致性很重要,你就應該使用引用式結構。例如,多個文檔需要原子的查看資料的變化。如果我們正在設計一個交易系統,有價證券只能在特定的時間才可以進行交易,到達不可以進行交易的時間,我們需要立即鎖定這些有價證券。這種事情在應用層級來操作可能更好,因為應用需要知道在什麼時間鎖定或者解鎖。
在上面的這個訂單應用中,一致性可能是有害的。假設我們想要給一個產品打折,20% off。我們不想更新已經存在的訂單中的產品資訊。這時候,我們需要的可能是一個快照,是下單時候的產品資訊。
如果需要儘可能快的讀取速度,我們應該使用嵌入式結構。即時系統應該更多的使用嵌入式結構。
上面的這個訂單文檔是一個很好的嵌入式結構的例子,改變產品資訊的時候,我們不想改變訂單的資訊。在這裡,引用式結構不能帶給我們任何的好處。
在這個例子中,嵌入式結構是最佳的選擇。
最後給大家一個地址,Your Coffee Shop Doesn't Use Two-Phase Commit,,裡面的例子講述了在真實的環境中,如何處理一致性問題,以及相關的系統改如何設計。