參考連接:learnmongodbthehardway.com/schema/sche…mongodb
Mongodb是文檔型數據庫,因爲其不屬於關係型數據庫,沒必要遵照三大範式,並且也沒有Join關鍵字來支持錶鏈接,因此Mongodb的表結構設計跟Oracle、MySQL很不同。下面針對幾種不一樣的表設計結構分別舉例:數據庫
在關係型數據庫中,1對1關係模型一般是經過外鍵的形式進行處理。咱們以做家跟地址來舉例,假設這兩個實體的關係是1對1,那麼咱們可能會像下面這樣子建表數組
可是,爲了方便,其實咱們在設計表的時候不會嚴格遵照三大範式,會作必定的冗餘數據,實際狀況下可能就是這樣子的表bash
那麼,咱們回到Mongodb,在這張非關係型的NoSQL數據庫裏,沒有標準的外鍵(雖然咱們能夠手工創建鏈接,可是這種表之間的字段關聯關係只能存在程序級別,數據庫自己並無提出外鍵約束的概念),咱們能夠怎麼來創建表並處理表之間的關係呢?架構
創建鏈接post
這種方式能夠理解爲創建外鍵,在其中一個表裏創建對方的id字段性能
用戶信息的文檔設計
{
_id: 1,
name: "Peter Wilkinson",
age: 27
}
保留外鍵的地址信息的文檔設計
{
user_id: 1,
street: "100 some road",
city: "Nevermore"
}複製代碼
內嵌文檔優化
直接把地址信息的文檔做爲用戶信息文檔的一個字段儲存進去url
{
name: "Peter Wilkinson",
age: 27,
address: {
street: "100 some road",
city: "Nevermore"
}
}複製代碼
直接內嵌文檔的好處就是咱們能夠在單次讀操做就能夠特定用戶的用戶信息文檔以及對應的地址信息文檔,固然了,這是在用戶信息和地址信息強關聯的時候,這樣子直接內嵌才顯得有意義。spa
官方文檔推薦1對1的數據模型儘可能使用內嵌的方式,這樣子會提升讀操做的效率,更快地獲取文檔信息複製代碼
1對多的關係模型,咱們能夠簡單地以博客和對應的評論信息來舉例
對應的Mongodb的表模型以下
博客信息的文檔設計
{
title: "An awesome blog",
url: "http://awesomeblog.com",
text: "This is an awesome blog we have just started"
}
評論信息的文檔設計
{
name: "Peter Critic",
created_on: ISODate("2014-01-01T10:01:22Z"),
comment: "Awesome blog post"
}
{
name: "John Page",
created_on: ISODate("2014-01-01T11:01:22Z"),
comment: "Not so awesome blog"
}複製代碼
在關係型數據庫裏,咱們一般是分別創建兩張表:一個Blog表、一個Comments表(從表,帶有blog_id外鍵),而後經過join操做把兩個表關聯起來
可是在Mongodb裏因爲沒有Join關鍵字,可是咱們能夠根據Mongodb的特色,得出如下三個解決方式:
內嵌
內嵌了評論信息的博客文檔設計
{
title: "An awesome blog",
url: "http://awesomeblog.com",
text: "This is an awesome blog we have just started",
comments: [{
name: "Peter Critic",
created_on: ISODate("2014-01-01T10:01:22Z"),
comment: "Awesome blog post"
}, {
name: "John Page",
created_on: ISODate("2014-01-01T11:01:22Z"),
comment: "Not so awesome blog"
}]
}複製代碼
上面這種表設計的好處是,咱們能夠直接獲取指定博客下的評論信息,用戶新增評論的話,直接在blog文檔下的comments數組字段插入一個新值便可。
可是這種表設計至少有三個以下的潛在問題須要注意:
必須注意的是,只有高寫入流量的狀況下才可能會影響寫性能,而對於寫入流量較小的程序反而沒有那麼大的影響。視具體狀況而定。
3. 第三個問題是當你嘗試去進行評論分頁的時候,你會發覺經過常規的find查詢操做,咱們只能先讀取整個文檔信息(包括全部評論信息),而後在程序裏進行評論信息的分頁複製代碼
鏈接
第二個方式是經過創建相似外鍵的id來進行文檔間的關聯
博客的文檔設計
{
_id: 1,
title: "An awesome blog",
url: "http://awesomeblog.com",
text: "This is an awesome blog we have just started"
}
評論的文檔設計
{
blog_entry_id: 1,
name: "Peter Critic",
created_on: ISODate("2014-01-01T10:01:22Z"),
comment: "Awesome blog post"
}
{
blog_entry_id: 1,
name: "John Page",
created_on: ISODate("2014-01-01T11:01:22Z"),
comment: "Not so awesome blog"
}複製代碼
這樣子設計模型有個好處是當評論信息逐漸增加的時候並不會影響原始的博客文檔,從而避免了單個文檔超過16MB的狀況出現。並且這樣子設計也比較容易返回分頁評論。可是壞處的話,就是假設咱們在一個博客文檔下擁有很是多的評論時(好比1000條),那咱們獲取全部評論的時候會引發數據庫不少的讀操做複製代碼
分塊
第三個方法就是前面兩種方法的混合,理論上,嘗試去平衡內嵌策略和鏈接模式,舉個例子,咱們可能會根據實際狀況,把全部的評論切分紅最多50條評論的分塊
博客的文檔設計
{
_id: 1,
title: "An awesome blog",
url: "http://awesomeblog.com",
text: "This is an awesome blog we have just started"
}
評論信息的文檔設計
{
blog_entry_id: 1,
page: 1,
count: 50,
comments: [{
name: "Peter Critic",
created_on: ISODate("2014-01-01T10:01:22Z"),
comment: "Awesome blog post"
}, ...]
}
{
blog_entry_id: 1,
page: 2,
count: 1,
comments: [{
name: "John Page",
created_on: ISODate("2014-01-01T11:01:22Z"),
comment: "Not so awesome blog"
}]
}複製代碼
這樣子設計最大的好處是咱們能夠單次讀操做裏一次性抓出50條評論,方便咱們進行評論分頁
何時使用分塊策略?
當你能夠將文檔切割成不一樣的批次時,那麼採用這種策略能夠加速文檔檢索
典型的例子就是根據小時、天數或者數量進行評論分頁(相似評論分頁)複製代碼
多對多關係模型,咱們以做者跟創做的書籍來舉例
雙向嵌套
在MongoDB裏咱們能夠經過雙向嵌套,把兩個文檔的外鍵經過數組字段添加到彼此的文檔裏
做者信息的文檔設計
{
_id: 1,
name: "Peter Standford",
books: [1, 2]
}
{
_id: 2,
name: "Georg Peterson",
books: [2]
}
書籍信息的文檔設計
{
_id: 1,
title: "A tale of two people",
categories: ["drama"],
authors: [1, 2]
}
{
_id: 2,
title: "A tale of two space ships",
categories: ["scifi"],
authors: [1]
}複製代碼
當咱們進行查詢的時候,能夠經過兩個維度互相進行查詢
經過指定的做者搜索對應的書籍
var db = db.getSisterDB("library");
var booksCollection = db.books;
var authorsCollection = db.authors;
var author = authorsCollection.findOne({name: "Peter Standford"});
var books = booksCollection.find({_id: {$in: author.books}}).toArray();
經過指定的書籍搜索對應的做者
var db = db.getSisterDB("library");
var booksCollection = db.books;
var authorsCollection = db.authors;
var book = booksCollection.findOne({title: "A tale of two space ships"});
var authors = authorsCollection.find({_id: {$in: book.authors}}).toArray();複製代碼
單向嵌套
單向嵌套策略是用來優化多對多關係模型裏的讀性能,經過將雙向引用轉移爲相似一對多的單向引用。這種策略是有特定場景的,好比在咱們這個案例中,咱們設計的做者信息文檔裏,將書籍信息做爲數組字段嵌入做者文檔,可是實際狀況下,書籍的數量是會快速地增加,極可能會突破單個文檔16MB的限制。
在這個案例中,咱們能夠看到書籍數量是快速增加的,可是書籍分類確實比較固定,一般不會有太大改動,因此咱們把書籍分類信息單獨設計成文檔,而後做者信息做爲書籍信息的嵌入數組引用,書籍分類也做爲嵌入數組引用。以相對變化不大的書籍分類做爲主表,把相對變化較大的書籍信息做爲從表,儲存主表id做爲外鍵。
書籍分類的文檔設計
{
_id: 1,
name: "drama"
}
經過外鍵關聯對應分類的書籍信息文檔設計
{
_id: 1,
title: "A tale of two people",
categories: [1],
authors: [1, 2]
}複製代碼
相對應的查詢語句以下
經過指定書籍來查找對應的書籍分類
var db = db.getSisterDB("library");
var booksCol = db.books;
var categoriesCol = db.categories;
var book = booksCol.findOne({title: "A tale of two space ships"});
var categories = categoriesCol.find({_id: {$in: book.categories}}).toArray(); 複製代碼
根據指定書籍分類來查找對應書籍
var db = db.getSisterDB("library");
var booksCollection = db.books;
var categoriesCollection = db.categories;
var category = categoriesCollection.findOne({name: "drama"});
var books = booksCollection.find({categories: category.id}).toArray();
須要注意的地方:
保持關聯關係的平衡
當多對多關係模型裏,有一個模型數量級別特別大(好比最多可達500000個),另外一個數量級別特別小(好比最多3個),像這個案例中,可能才3個左右的書籍分類就能夠對應到高達500000本書。在這張數量級別懸殊的狀況下,就應該採用這種單向嵌套的策略。若是雙方書籍級別都比較小(可能最多也就是5個左右)的時候,採用雙向嵌套的策略可能會好一點。複製代碼