在MongoDB尋求資料正規化是否搞錯了什麼?
MongoDB資料模型設計指南
今天想要跟大家分享一下 MongoDB 的 資料模型設計的基礎概念,幫助大家在使用上可以更簡單,更有效率。
MongoDB是Json Document Sotre的資料庫,屬於Flexible Data Model, 可以有著更好的效率進行資料的讀寫,同時讓使用者可以更彈性的制定專屬於應用的資料模型,可以不需要再一昧的要求正規化,能夠調整到最適合開發者的效益(簡易方便/效能最佳)。
如何設計MongoDB的資料模型?
首先你必須要了解一件事情,MongoDB可以實現多樣化的資料模型,例如 Key/Value,Sub Documents 等方式,所以資料模型在MongoDB裡面沒有標準答案。
永遠沒有最好的資料模型,只有最適合你的應用的資料模型。
Flexible Schema = No Schema ?
但是對於初入MongoDB的使用者來說,往往由於感覺資料模型太過於”彈性”而無所適從,不知道該如何下手,常常會問怎麼樣的資料模型才是最好的?
事實上沒有想像中的那麼複雜,大多情況就是依照開發者的“直覺”,再加上一些的美味秘方做調整就可以了,所以我也特別整理了一些自己在Model Design上的經驗與大家分享。
文檔資料模型特色
首先必須要說關聯式資料庫的正規化是有“優點”的,讓你在開發應用的時候就像在“抄作業”,雖然實行資料關聯相對繁瑣複雜,但如果熟悉後其實也不難,反正就是在套公式。
但同時關聯式資料庫的正規化,在微服務興起,需求快速改變的時代,缺點也比較明顯,那就是面對需求改變的時候,明顯應變不足。
以一個部落格為例子,若今天需求改變,使用者的興趣想要能夠儲存多個,則架構需要調整,興趣欄位必需單獨拉出來做資料正規化,需要透過Join來取得所有資料,程式碼需要調整,資料庫結構(DDL)需要改變。
而程式碼的改動與DDL的改動,在開發環境、測試環境、正式環境,一層一層跟著異動,時常會花費大量的時間做反覆的確認以免發生程式碼改了,DDL沒有跟著改的狀況,或是程式還沒上版DDL就先異動。
而如果使用文檔資料模型,少去了改動DDL的步驟,則可以輕鬆的面對改變帶來的挑戰。
當需求改變,只需要調整少數程式碼,把interest物件從單一值改為矩陣,然後隨著程式的上版,異動就完成了,實際上是真的非常簡單方便。
文檔資料模型無需再維護資料庫結構(DDL),提升開發效率。
也無需使用Join來取得關聯資料,不會有潛在的效能問題。
模型設計方法比較
所以從一般的RDB與MongoDB的模型設計理念比較來看,可以看到MongoDB的優勢在於“從服務應用開始著手,並且有著容易更改的特性”
所以文檔資料模型的設計方法會從應用行為定義開始,透過行為來決定資料關聯,與關聯式資料庫的理念剛好相反,更能夠從應用開發的角度出發。
資料關聯方式說明
提到了資料關聯,我想大家對於一般的RDB做Foreign Key,做Join應該都不陌生,而MongoDB如何做資料關聯呢?
MongoDB 現行主要使用兩種方式做資料關聯,分別為 Embedding 與 Referencing。
•Embedding
將關聯資料以子文檔(Subdocuments)的方式存在同一個資料表裡面,方便直接取用。
例如:Category, Comment, Tag•Referencing
將關聯資料獨立出來成一個文檔,適合分別存取使用,需要時可以透過MongoDB的Join功能 ($lookup) 來做關聯查詢。
例如:User
所以MongoDB在資料關聯的應用上是彈性的,而 Embedding 與 Referencing 的選擇,可依照使用行為的不同來決定,以下是關聯的使用優點與注意事項,至於有沒有懶人包呢? 有的。
那就是『盡量使用Embedding除非有效能問題』。
開始設計資料模型
在開始設計MongoDB的資料模型前,你必須要有已經定義好的資料實體與使用行為,以及最好有操作的頻率以及期望的效能來決定設計的走向。
例如今天我們想要設計電商購物車平台:
需要有哪些資料實體,例如:產品目錄,庫存,訂單,客戶,金流。
需要有哪些行為,例如:商品總覽,商品明細,庫存資訊,訂單資訊,會員管理。
整理並統計這些行為的頻率/併發數以及期望的回應時間與優先的程度 。
資料模型設計步驟
當相關的資料都準備好之後,接下來就是正式的 Data Model Design 的環節了,事實上只需要三個步驟,而初見MongoDB的開發者可以從左邊最簡單的的設計方法開始,就可以打造一個符合MongoDB建議的模型設計。
設計初始資料模型
例如一開始提到的部落格,主要的行為就是寫文章,則模型的設計主體就會以文章為出發點做設計,所以所有的資料關聯則就會被以Embeding的方式被納入文章主體中作一起存取。
資料模型調整
而當模型依照主要的使用方式設計完成後,接下來就是審視其他的使用方式與需求,來確認資料模型是否需要再調整。
在此部落格,我們除了文章主體以外,還有會員資料是需要單獨管理的,所以我們可以思考評估是否要把會員給單獨拆分出來,而最後的模型就會是我們依照需求所定義出來的。
套用模型最佳化指南
最後在設計完成後,如果還有餘力可以再參考 Design Pattern 與 Design Anti-Pattern 來思考是否有進一步優化的空間。
但這不是必要的步驟,也不常進行,通常在發生效能問題的時候才會去思考如何改善。
關於這兩種 Pattern ,由於篇幅的關係我會留到下次再跟各位做分享。
重點回顧
最後我們再來回顧一下今天分享的重點:
1.在大多數情況下,資料關聯建議使用Embeding。
2.除非資料有單獨存取的需求,再使用Referencing。
3.關聯資料存取盡量避免使用Join ($lookup),可以使用兩次查詢取代。
4.如果有使用Array,盡量避免儲存超過一百個。
5.視情況參考進階的Design Pattern或是Anti-Pattern
參考資源
對於MongoDB Model Design, 如果還有興趣了解更多,可以參考以下文章。
Data Modeling - MongoDB University https://university.mongodb.com/courses/M320/aboutMongoDB Blog - Design Best Practice https://developer.mongodb.com/article/mongodb-schema-design-best-practices/MongoDB Blog - Design Patterns https://www.mongodb.com/blog/post/building-with-patterns-a-summaryMongoDB Blog - Design Anti-Patterns https://developer.mongodb.com/article/schema-design-anti-pattern-summary/MongoDB Manual - Data Model https://docs.mongodb.com/manual/core/data-model-design/