MongoDB的文檔存儲結構
MongoDB文檔數據庫的存儲結構分為四個層次,從小到大依次是:鍵值對、文檔(document)、集合(collection)、數據庫(database)。
圖 1 描述了 MongoDB 的存儲與MySQL存儲的對應關系,可以看出,MongoDB中的文檔、集合、數據庫對應於關系數據庫中的行數據、表、數據庫。
圖 1:MongoDB 存儲與 Mysql 存儲的對比
鍵值對
文檔數據庫存儲結構的基本單位是鍵值對,具體包含數據和類型。鍵值對的數據包含鍵和值,鍵的格式一般為字符串,值的格式可以包含字符串、數值、數組、文檔等類型。
按照鍵值對的復雜程度,可以將鍵值對分為基本鍵值對和嵌套鍵值對。
- 圖 2 中的鍵值對中的鍵為字符串,值為基本類型,這種鍵值對就稱為基本鍵值。
- 嵌套鍵值對類型如圖 3 所示,從圖中可以看岀, contact 的鍵對應的值為一個文檔,文檔中又包含了相關的鍵值對,這種類型的鍵值對稱為嵌套鍵值對。
圖 2:MongoDB 文檔數據模型
圖 3:嵌套鍵值對
鍵(Key)起唯一索引的作用,確保一個鍵值結構里數據記錄的唯一性,同時也具有信息記錄的作用。例如,country:"China",用:
實現了對一條地址的分割記錄,“country”起到了 “China”的唯一地址作用,另外,“country”作為鍵的內容說明了所對應內容的一些信息。
值(Value)是鍵所對應的數據,其內容通過鍵來獲取,可存儲任何類型的數據,甚至可以為空。
鍵和值的組成就構成了鍵值對(Key-Value Pair)。它們之間的關系是一一對應的,如定義了 “country:China”鍵值對,"country”就只能對應“China”,而不能對應“USA”。
文檔中鍵的命名規則如下。
- UTF-8 格式字符串。
- 不用有
\0
的字符串,習慣上不用.
和$
。 - 以開頭的多為保留鍵,自定義時一般不以開頭。
- 文檔鍵值對是有序的,MongoDB 中嚴格區分大小寫。
文檔
文檔是 MongoDB 的核心概念,是數據的基本單元,與關系數據庫中的行十分類似,但是比行要復雜。文檔是一組有序的鍵值對集合。文檔的數據結構與 JSON 基本相同,所有存儲在集合中的數據都是 BSON 格式。
BSON 是一種類 JSON 的二進制存儲格式,是 Binary JSON 的簡稱。 一個簡單的文檔例子如下:
{"country" : "China", "city": "BeiJing"}
MongoDB 中的數據具有靈活的架構,集合不強制要求文檔結構。但數據建模的不同可能會影響程序性能和數據庫容量。文檔之間的關系是數據建模需要考慮的重要因素。文檔與文檔之間 的關系包括嵌入和引用兩種。
下面舉一個關於顧客 patron 和地址 address 之間的例子,來說明在某些情況下,嵌入優於引用。
{ _id: "joe", name: "Joe Bookreader"}{ patron_id: "joe", street: "123 Fake Street", city: "Faketon", state: "MA", zip: "2345"}
關系數據庫的數據模型在設計時,將 patron 和 address 分到兩個表中,在查詢時進行關聯, 這就是引用的使用方式。如果在實際查詢中,需要頻繁地通過 _id 獲得 address 信息,那么就需要頻繁地通過關聯引用來返回查詢結果。在這種情況下,一個更合適的數據模型就是嵌入。
將 address 信息嵌入 patron 信息中,這樣通過一次查詢就可獲得完整的 patron 和 address 信息,如下所示:
{ _id: "joe", name: "Joe Bookreader", address: { street: "123 Fake Street", city: "Faketon”, state: nMAnz zip: T2345” }}
如果具有多個 address,可以將其嵌入 patron 中,通過一次查詢就可獲得完整的 patron 和多個 address 信息,如下所示:
{ _id: "joe", name: "Joe Bookreader", addresses:[ { street: "123 Fake Streetn, city: "Faketon", state: "MA", zip: "12345" }, { street: "l Some Other Street", city: "Boston", state: "MA", zip: "12345" } ]}
但在某種情況下,引用用比嵌入更有優勢。下面舉一個圖書出版商與圖書信息的例子,代碼如下:
{ title: "MongoDB: The Definitive Guide", author: [ "Kristina Chodorow", "Mike Dirolfn"], published_date: ISODate("2010-09-24"), pages: 216, language: "English", publisher: { name: "O'Reilly Media", founded: 1980, location: "CA" }}{ title: "50 Tips and Tricks for MongoDB Developer", author: "Kristina Chodorow", published_date: ISODate("2011-05-06"), pages: 68, language: "English", publisher: { name: "O'Reilly Media", founded: 1980, location: "CA" }}
從上邊例子可以看出,嵌入式的關系導致出版商的信息重復發布,這時可采用引用的方式描述集合之間的關系。使用引用時,關系的增長速度決定了引用的存儲位置。如果每個出版商的圖書數量很少且增長有限,那么將圖書信息存儲在出版商文檔中是可行的。
通過 books 存儲每本圖書的 id 信息,就可以查詢到指定圖書出版商的指定圖書信息,但如果圖書出版商的圖書數量很多, 則此數據模型將導致可變的、不斷增長的數組 books,如下所示:
{ name: "O'Reilly Media", founded: 1980, location: "CA", books: [123456789, 234567890, …]}{ _id: 123456789, title: "MongoDE: The Definitive Guide", author: ["Kristina Chodorow", "Mike Dirolf"], published_date: ISODate("2010-09-24"), pages: 216, language: "English"}{ _id: 234567890, title: "50 Tips and Tricks for MongoDB Developer", author: "Kristina Chodorow", published_date: ISODate("2011-05-06"), pages: 68, language: "English"}
為了避免可變的、不斷增長的數組,可以將出版商引用存放到圖書文檔中,如下所示:
{ _id: "oreilly", name: "O'Reilly Media", founded: 1980, location: "CA"}{ _id: 123456789, title: "MongoDB: The Definitive Guiden, author: [ "Kristina Chodorow", "Mike Dirolf"], published_date: ISODate("2010-09-24"), pages: 216, language: "English", publisher_id: "oreilly"}{ _id: 234567890, title: "50 Tips and Tricks for MongoDB Developer", author: "Kristina Chodorow", published date: ISODate("2011-05-06"), pages: 68, language: "English", publisher_id: "oreilly"}
集合
MongoDB 將文檔存儲在集合中,一個集合是一些文檔構成的對象。如果說 MongoDB 中的文檔類似於關系型數據庫中的“行”,那么集合就如同“表”。
集合存在於數據庫中,沒有固定的結構,這意味着用戶對集合可以插入不同格式和類型的數據。但通常情況下插入集合的數據都會有一定的關聯性,即一個集合中的文檔應該具有相關性。
集合的結構如圖 4 所示。
圖 4:文檔數據庫中的一個集合
數據庫
在 MongoDB 中,數據庫由集合組成。一個 MongoDB 實例可承載多個數據庫,互相之間彼此獨立,在開發過程中,通常將一個應用的所有數據存儲到同一個數據庫中,MongoDB 將不同數據庫存放在不同文件中。
數據庫結構示例如圖 5 所示。
圖 5:一個名為 DB 的數據庫的結構