2019年1月29日 星期二

MongoDB document schema 設計思維入門

本文摘要自Thinking in Documents: Part 2.

此文章將介紹MongoDB的Document schema設計思維,包含如何用embedding及referencing來整理相關資料,也會介紹一點索引及MongoDB的transaction model。

定義你自己的Document Schema

首先,最重要的是你的app查詢資料的模式,也別忘記善用document model的彈性,意即其embedding和豐富的BSON based資料結構支援。

app資料存取的模式首要在以下幾點:

1. 資料庫的讀寫比(R/W ratio)
2. 資料庫所做的查詢指令及資料更新的類型(type of queries and updates)
3. 資料的生命週期以及document的成長速度

若你有關聯式資料庫的背景,可以先想像以下兩點:

1. 你會如何使用關聯式DB來實作這些操作。
2. 在MongoDB要怎麼實作。

另外也可以透過RDBMS的日誌來分析最常使用的查詢指令或是最常被共同查詢的資料,作為是否合併在單一document下的考量。

使用Embedding或Referencing來建立關聯

直接embed或是對其他collection的document做reference的時機並無絕對,但是還是有一些準則可供參考。

Embedding

對於一對一(one-to-one)或是一對多(one-to-many)的對應關係,很適合使用embedding模式,因為這些資料很可能本身就是依存在parent document之下的一種特性或是資訊。此時parent document就像是data owner或是container一樣。可以想像當parent document消滅,若該document將會失去意義的話,就是屬於這種類型。

若是需要被一起更動(update atomically)的資料,也應該使用此模式,詳細可參照下面關於Transaction model的說明。

然而,並不是所有的一對一或是一對多對應關係都適合embedding。舉例來說,以下的時機適合使用referencing:

1. 當一個document很頻繁被讀取,但是其內的embedded document卻幾乎不會被存取到。舉例來說,一個顧客紀錄document之中的年度總報告。由於年度總報告並不會被經常使用,嵌入它只會增加該collection消耗的記憶體。
2. 某一document其中一部分很頻繁被更新且不斷成長,但是剩下的部份卻相對沒有變動。
3. document size超過MongoDB目前的 16 MB限制。

Referencing

Referencing讓資料正規化(data normalization)變得可能,也比embedding有彈性。但是MongoDB伺服器卻需要追加查詢來解析關聯,因此會需要多次讀取動作,消耗較多時間。

實作上通常是在一個document中儲存另一個document的_id field當作參照,接著再由app執行查詢來取得參照的資料。

Referencing應該被用在:

1. 當使用embedding會造成資料冗餘(data duplication),無法期待效能向上時。
2. 物件從各種不同來源被關聯時。
3. 多對多(many-to-many)關係時。
4. 大型、階層式的資料集時。

不同的設計目標

從以上觀察我們可以發現RDBMS和document model的根本上差異:

1. RDBMS對資料的處理是以儲存空間效率來考量(因為早期儲存空間是系統中成本最高的元件)。
2. MongoDB的document model則是以app存取資料的效率為考量(因為開發者的時間和上線速度現在比儲存空間還要受重視)。

MongoDB Transaction Model

MongoDB提供了如關聯式DB的document-level ACID compliance,包含可以atomically update embedded arrays以及sub-document,且無須付出如RDBMS的耗時ACID operation,以及在不同table之間維護關聯完整性(referential integrity)。

Document-level ACID compliance保證在document改動時的隔離性(isolation),任何錯誤都會讓資料還原到操作前的樣貌,且用戶端也會收到原本的document view。

NOTE:在 MongoDB 4.0之後,加入了 multi-document ACID transactions來確保replica sets之間的資料是一致的,並確保任何執行都是「有或全無(all-or-nothing)」來維護資料完整性。在4.0版本之前,您還是可以透過findandmodify或是two-phase commit來達到一樣的目的。

如何找出我的資料?

MongoDB使用B-tree indexes,並原生支援secondary indexes。因此,SQL背景的人很快就能上手。不過索引也還是一樣會在寫入及資源使用上增加負擔,如同所有的資料庫。預設MongoDB會在document的 _id欄位建立索引,其他user-defined indexes都是secondary indexes。
任何欄位都能當作secondary index,包含在arrays中的欄位。在MongoDB中的索引有:

1. Compound Indexes
2. Geospatial Indexes
3. Text Search Indexes
4. Unique Indexes
5. Array Indexes
6. TTL Indexes
7. Sparse Indexes
8. Hash Indexes

另外,MongoDB也支援index intersection以使用多個索引做查詢。

沒有留言:

張貼留言