在MongoDB尋求資料正規化是否搞錯了什麼?

Caspar Chang
8 min readJul 3, 2021

--

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 現行主要使用兩種方式做資料關聯,分別為 EmbeddingReferencing

•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/

--

--

Caspar Chang

具有十年以上的資料庫開發與維運管理經驗,現任職於 MongoDB Taiwan 解決方案架構師