近些年,No-SQL數據庫興起,在存儲與處理大數據方面具備天然的優勢。但對于如何設計No-SQL數據庫的資料少之又少,很多人都是在邊實踐、邊學習,踩過不少坑。MongoDB是目前主流的No-SQL之一,文檔關系的設計是其中常見的難點。本系列文章將從基礎談起,聊聊如何將文檔關系設計做好。本文是此系列文章的第一篇,介紹文檔關系設計的基礎知識。
當開始設計MongoDB的結構(schema)時,你需要考慮一個在SQL關系型數據庫(RMDBS)不會遇到的問題:文檔之間的數量對應關系。具體來說,你需要思考one-to-N關系是one-to-few,one-to-many或者one-to-squillions三種中的哪種。根據具體的對應關系不同,你將會使用不同的關系模型。
1. 基本模型:One-to-Few
一個one-to-few的例子就是一個人的地址。在這個例子中,地址文檔(Address)適合被內嵌(embedded)在人(Person)的文檔中,可以將Address文檔以數組的方式存儲在Person的文檔中。
這種設計具有內嵌關系的優點和缺點。優點在于你無須利用一個單獨的查詢來讀取內嵌文檔的細節;主要的缺點在于你無法作為一個單獨的實體來訪問內嵌文檔的細節。
在人-地址的例子中,你可以非常方便查詢到一個人名下的所有地址,但對于查詢所有地址在某個城市的人,將會是相當困難。
2. 基本模型:One-to-Many
一個典型one-to-many的例子就是電商系統中的顧客與訂單之間的關系,而且隨著時間的增加,顧客的訂單數會逐漸增長,會達到上百,甚至上萬個。在這種情形下,非常適合使用引用(reference),將訂單的ObjectIDs存儲在一個數組中,然后內嵌在顧客的文檔中。
在這種設計中,每個訂單是一個文檔,每個顧客也是一個文檔,而且包含一個指向訂單引用的數組。那么為了獲取某個顧客的所有訂單,可以通過應用程序層面(application-level)的關聯操作來實現。為了提高查詢效率,需要對顧客的_id建立索引。由于orders._id本身就存在索引(因為_id存儲在一個數組中),所以對訂單的查詢效率通常很高。
這種內嵌文檔引用的方式既有好處,也有壞處。由于每個子文檔(訂單)是一個獨立的文檔,因而對于搜索與更新子文檔(訂單)來說,非常方便。但這種設計的一個不足之處在于,需要兩個查詢才能獲得子文檔(訂單)的詳情。比如要查詢名為張三顧客的未付款訂單詳情:第一步,根據姓名查詢顧客集合,獲取顧客張三的文檔;第二步,根據查的顧客文檔,再查詢所有未付款訂單詳情。
這種設計的一個額外好處,就是可以拓展到N-to-N關系模型,由于子文檔是獨立存在的文檔,那么子文檔可以內嵌在多個不同的父文檔中。比如在一個學校中,學生與老師之間是一個N-to-N關系,老師可以給多名學生授課,一名學生可以擁有多名授課老師。
3. 基本模型:One-to-Squillions
這是一對超多的關系,這個超多的概念可以理解為千萬級別的數據量。一個常見的例子就是應用的日志系統,日志系統收集分布式主機上的各種事件日志,這個數據非常大,而且增長迅速。即使采用日志文檔引用數組的方式進行存儲,任何一臺主機上的日志數量可以很容易超過16MB的文檔大小。此時,非常適合采用常見的父引用(parent-referencing)方式進行存儲:每臺主機擁有一個文檔,然后將主機日志文檔的引用存儲在日志消息的文檔中。
在此種情形下,你可以利用應用程序級別上的關聯查詢,來獲取某個主機上的最近5000條日志。
說明:對于BSON-document方式存儲的文件,其大小的限制是16MB,如果希望存儲更大數據大小的集合文件,可以考慮GridFS方式。更多關于GridFS的說明,可以參考官方文檔。
總結
以上是三種基本的one-to-N關系模型,當你設計MongoDB的數據庫結構時,你需要考慮兩個因素:
- 對于N類文檔,是否需要單獨被存儲?
- 文檔之間的具體關系是什么,是one-to-few,one-to-many,還是one-to-sequillions?
基于這兩方面的因素,你可以選擇上述三種的一種方式進行設計:
- 如果是one-to-few關系,并且無須從父文檔之外的方式訪問N類文檔,內嵌N類文檔;
- 如果是one-to-many關系,并且希望N類文檔以獨立的文檔存儲,那么使用N類文檔的引用數組,并內嵌在父文檔中;
- 如果是one-to-sequillions關系,在N類文檔中存儲一個指向one類文檔的引用。
N類文檔,代表處在N這邊的文檔,在一個顧客訂單(one-to-many)關系中,N類文檔指訂單文檔,one類文檔代指顧客文檔
如果覺得此文能夠給你帶來幫助和啟發,請不要吝嗇你的贊_。同時關注該該專題,后面的章節會陸續奉獻給大家。祝各位周末愉快!