1.MongDB 簡介
MongoDB(來自于英文單詞“Humongous”苟穆,中文含義為“龐大”)是可以應(yīng)用于各種規(guī)模的企業(yè)、各個行業(yè)以及各類應(yīng)用程序的開源數(shù)據(jù)庫诀豁。作為一個適用于敏捷開發(fā)的數(shù)據(jù)庫胎源,MongoDB 的數(shù)據(jù)模式可以隨著應(yīng)用程序的發(fā)展而靈活地更新鲫寄。與此同時耻卡,它也為開發(fā)人員 提供了傳統(tǒng)數(shù)據(jù)庫的功能:二級索引疯汁,完整的查詢系統(tǒng)以及嚴(yán)格一致性等等。 MongoDB 能夠使企業(yè)更加具有敏捷性和可擴展性劲赠,各種規(guī)模的企業(yè)都可以通過使用 MongoDB 來創(chuàng)建新的應(yīng)用,提高與客戶之間的工作效率秸谢,加快產(chǎn)品上市時間凛澎,以及降低企業(yè)成本。
MongoDB 是專為可擴展性估蹄,高性能和高可用性而設(shè)計的數(shù)據(jù)庫塑煎。它可以從單服務(wù)器部署擴展到大型、復(fù)雜的多數(shù)據(jù)中心架構(gòu)臭蚁。利用內(nèi)存計算的優(yōu)勢最铁,MongoDB 能夠提供高性能的數(shù)據(jù)讀寫操作。 MongoDB 的本地復(fù)制和自動故障轉(zhuǎn)移功能使您的應(yīng)用程序具有企業(yè)級的可靠性和操作靈活性垮兑。
以上內(nèi)容摘自官網(wǎng):
1.1 文檔型數(shù)據(jù)庫
簡而言之冷尉,MongoDB是一個免費開源跨平臺的 NoSQL 數(shù)據(jù)庫,與關(guān)系型數(shù)據(jù)庫不同系枪,MongoDB 的數(shù)據(jù)以類似于 JSON 格式的二進(jìn)制文檔存儲:
{
name: "我沒有三顆心臟",
age: 22,
}
文檔型的數(shù)據(jù)存儲方式有幾個重要好處:
- 文檔的數(shù)據(jù)類型可以對應(yīng)到語言的數(shù)據(jù)類型雀哨,如數(shù)組類型(Array)和對象類型(Object);
- 文檔可以嵌套,有時關(guān)系型數(shù)據(jù)庫涉及幾個表的操作雾棺,在 MongoDB 中一次就能完成膊夹,可以減少昂貴的連接花銷;
- 文檔不對數(shù)據(jù)結(jié)構(gòu)加以限制捌浩,不同的數(shù)據(jù)結(jié)構(gòu)可以存儲在同一張表放刨;
- MongoDB 的文檔數(shù)據(jù)模型和索引系統(tǒng)能有效提升數(shù)據(jù)庫性能;
- 復(fù)制集功能提供數(shù)據(jù)冗余尸饺,自動化容災(zāi)容錯进统,提升數(shù)據(jù)庫可用性;
- 分片技術(shù)能夠分散單服務(wù)器的讀寫壓力侵佃,提高并發(fā)能力麻昼,提升數(shù)據(jù)庫的可拓展性;
- MongoDB 高性能馋辈,高可用性抚芦、可擴展性等特點,使其至 2009 年發(fā)布以來迈螟,逐漸被認(rèn)可叉抡,并被越來越多的用于生產(chǎn)環(huán)境中。AWS答毫、GCP褥民、阿里云等云平臺都提供了十分便捷的 MongoDB 云服務(wù)。
1.2 MongoDB 基礎(chǔ)概念
可以使用我們熟悉的 MySQL 數(shù)據(jù)庫來加以對比:
MySQL 基礎(chǔ)概念 | MongoDB 對應(yīng)概念 |
---|---|
數(shù)據(jù)庫(database) | 容器(database) |
表(table) | 集合(collection) |
行(row) | 文檔(document) |
列(column) | 域(filed) |
索引(index) | 索引(index) |
也借用一下菜鳥教程的圖來更加形象生動的說明一下:
這很容易理解洗搂,但是問題在于:我們?yōu)槭裁匆胄碌母拍钅兀?/strong>(也就是為什么我們要把“表”替換成“集合”消返,“行”替換成“文檔”,“列”替換成“域”呢耘拇?)原因在于撵颊,其實在 MySQL 這樣的典型關(guān)系型數(shù)據(jù)中,我們是在定義表的時候定義列的惫叛,但是由于上述文檔型數(shù)據(jù)庫的特點倡勇,它允許文檔的數(shù)據(jù)類型可以對應(yīng)到語言的數(shù)據(jù)類型,所以我們是在定義文檔的時候才會定義域的嘉涌。
也就是說妻熊,集合中的每個文檔都可以有獨立的域。因此仑最,雖說集合相對于表來說是一個簡化了的容器扔役,而文檔則包含了比行要多得多的信息。
2 搭建環(huán)境
怎么樣都好警医,搭建好環(huán)境就行厅目,這里以 OS 環(huán)境為例,你可以使用 OSX 的 brew 安裝 mongodb:
brew install mongodb
在運行之前我們需要創(chuàng)建一個數(shù)據(jù)庫存儲目錄 /data/db
:
sudo mkdir -p /data/db
然后啟動 mongodb,默認(rèn)數(shù)據(jù)庫目錄即為 /data/db
(如果不是损敷,可以使用 --dbpath
指令來指定):
sudo mongd
過一會兒你就能看到你的 mongodb 運行起來的提示:
具體的搭建過程可以參考菜鳥的教程:http://www.runoob.com/mongodb/mongodb-window-install.html
3 基于 Shell 的 CRUD
3.1 連接實例
通過上面的步驟我們在系統(tǒng)里運行了一個 mongodb 實例葫笼,接下來通過 mongo
命令來連接它:
mongo [options] [db address] [file names]
由于上面運行的 mongodb 運行在 27017 端口,并且滅有啟動安全模式拗馒,所以我們也不需要輸入用戶名和密碼就可以直接連接:
mongo 127.0.0.1:27017
或者通過 --host
和 --port
選項指定主機和端口路星。一切順利的話,就進(jìn)入了 mongoDB shell
诱桂,shell
會報出一連串權(quán)限警告洋丐,不過不用擔(dān)心,這并不會影響之后的操作挥等。在添加授權(quán)用戶和開啟認(rèn)證后友绝,這些警告會自動消失。
3.2 CRUD 操作
在進(jìn)行增刪改查操作之前肝劲,我們需要先了解一下常用的 shell
命令:
-
db
顯示當(dāng)前所在數(shù)據(jù)庫迁客,默認(rèn)為test
-
show dbs
列出可用數(shù)據(jù)庫 -
show tables
show collections
列出數(shù)據(jù)庫中可用集合 -
use <database>
用于切換數(shù)據(jù)庫
mongoDB 預(yù)設(shè)有兩個數(shù)據(jù)庫,admin 和 local辞槐,admin 用來存放系統(tǒng)數(shù)據(jù)掷漱,local 用來存放該實例數(shù)據(jù),在副本集中榄檬,一個實例的 local 數(shù)據(jù)庫對于其它實例是不可見的卜范。使用 use 命令切換數(shù)據(jù)庫:
> use admin
> use local
> use newDatabase
可以 use 一個不存在的數(shù)據(jù)庫,當(dāng)你存入新數(shù)據(jù)時鹿榜,mongoDB 會創(chuàng)建這個數(shù)據(jù)庫:
> use newDatabase
> db.newCollection.insert({x:1})
WriteResult({ "nInserted" : 1 })
以上命令向數(shù)據(jù)庫中插入一個文檔海雪,返回 1 表示插入成功,mongoDB 自動創(chuàng)建 newCollection 集合和數(shù)據(jù)庫 newDatabase舱殿。下面將對增查改刪操作進(jìn)行一個簡單的演示奥裸。
3.2.1 創(chuàng)建(Create)
MongoDB 提供 insert 方法創(chuàng)建新文檔:
-
db.collection.inserOne()
插入單個文檔
WriteResult({ "nInserted" : 1 }) -
db.collection.inserMany()
插入多個文檔 -
db.collection.insert()
插入單條或多條文檔
我們接著在剛才新創(chuàng)建的 newDatabase 下面新增數(shù)據(jù)吧:
db.newCollection.insert({name:"wmyskxz",age:22})
根據(jù)以往經(jīng)驗應(yīng)該會覺得蠻奇怪的,因為之前在這個集合中插入的數(shù)據(jù)格式是 {x:1}
的怀薛,而這里新增的數(shù)據(jù)格式確是 {name:"wmyskxz",age:22}
這個樣子的刺彩。還記得嗎迷郑,文檔型數(shù)據(jù)庫的與傳統(tǒng)型的關(guān)系型數(shù)據(jù)的區(qū)別就是在這里枝恋!
并且要注意,age:22
和 age:"22"
是不一樣的哦嗡害,前者插入的是一個數(shù)值焚碌,而后者是字符串,我們可以通過 db.newCollection.find()
命令查看到剛剛插入的文檔:
> db.newCollection.find()
{ "_id" : ObjectId("5cc1026533907ae66490e46c"), "x" : 1 }
{ "_id" : ObjectId("5cc102fb33907ae66490e46d"), "name" : "wmyskxz", "age" : 22 }
這里有一個神奇的返回霸妹,那就是多了一個叫做 _id
的東西十电,這是 MongoDB 為你自動添加的字段,你也可以自己生成。大部分情況下還是會讓 MongoDB 為我們生成鹃骂,而且默認(rèn)情況下台盯,該字段是被加上了索引的。
3.2.2 查找(Read)
MongoDB 提供 find 方法查找文檔畏线,第一個參數(shù)為查詢條件:
> db.newCollection.find() # 查找所有文檔
{ "_id" : ObjectId("5cc1026533907ae66490e46c"), "x" : 1 }
{ "_id" : ObjectId("5cc102fb33907ae66490e46d"), "name" : "wmyskxz", "age" : 22 }
> db.newCollection.find({name:"wmyskxz"}) # 查找 name 為 wmyskxz 的文檔
{ "_id" : ObjectId("5cc102fb33907ae66490e46d"), "name" : "wmyskxz", "age" : 22 }
> db.newCollection.find({age:{$gt:20}}) # 查找 age 大于 20 的文檔
{ "_id" : ObjectId("5cc102fb33907ae66490e46d"), "name" : "wmyskxz", "age" : 22 }
上述代碼中的$gt對應(yīng)于大于號>的轉(zhuǎn)義静盅。
第二個參數(shù)可以傳入投影文檔映射數(shù)據(jù):
> db.newCollection.find({age:{$gt:20}},{name:1})
{ "_id" : ObjectId("5cc102fb33907ae66490e46d"), "name" : "wmyskxz" }
上述命令將查找 age
大于 20 的文檔,返回 name
字段寝殴,排除其他字段蒿叠。投影文檔中字段為 1 或其他真值表示包含,0 或假值表示排除蚣常,可以設(shè)置多個字段位為 1 或 0市咽,但不能混合使用。
為了測試抵蚊,我們?yōu)檫@個集合弄了一些奇奇怪怪的數(shù)據(jù):
> db.newCollection.find()
{ "_id" : ObjectId("5cc1026533907ae66490e46c"), "x" : 1 }
{ "_id" : ObjectId("5cc102fb33907ae66490e46d"), "name" : "wmyskxz", "age" : 22 }
{ "_id" : ObjectId("5cc108fb33907ae66490e46e"), "name" : "wmyskxz-test", "age" : 22, "x" : 1, "y" : 30 }
然后再來測試:
> db.newCollection.find({age:{$gt:20}},{name:1,x:1})
{ "_id" : ObjectId("5cc102fb33907ae66490e46d"), "name" : "wmyskxz" }
{ "_id" : ObjectId("5cc108fb33907ae66490e46e"), "name" : "wmyskxz-test", "x" : 1 }
> db.newCollection.find({age:{$gt:20}},{name:0,x:0})
{ "_id" : ObjectId("5cc102fb33907ae66490e46d"), "age" : 22 }
{ "_id" : ObjectId("5cc108fb33907ae66490e46e"), "age" : 22, "y" : 30 }
> db.newCollection.find({age:{$gt:20}},{name:0,x:1})
Error: error: {
"ok" : 0,
"errmsg" : "Projection cannot have a mix of inclusion and exclusion.",
"code" : 2,
"codeName" : "BadValue"
}
從上面的命令我們就可以把我們的一些想法和上面的結(jié)論得以驗證施绎,perfect!
除此之外泌射,還可以通過 count
粘姜、skip
、limit
等指針(Cursor)方法熔酷,改變文檔查詢的執(zhí)行方式:
> db.newCollection.find().count()
3
> db.newCollection.find().skip(1).limit(10).sort({age:1})
{ "_id" : ObjectId("5cc102fb33907ae66490e46d"), "name" : "wmyskxz", "age" : 22 }
{ "_id" : ObjectId("5cc108fb33907ae66490e46e"), "name" : "wmyskxz-test", "age" : 22, "x" : 1, "y" : 30 }
上述查找命令跳過 1 個文檔孤紧,限制輸出 10 個,以 age
子段正序排序(大于 0 為正序拒秘,小于 0 位反序)輸出結(jié)果号显。最后,可以使用 Cursor 方法中的 pretty 方法躺酒,提升查詢文檔的易讀性押蚤,特別是在查看嵌套的文檔和配置文件的時候:
> db.newCollection.find().pretty()
{ "_id" : ObjectId("5cc1026533907ae66490e46c"), "x" : 1 }
{
"_id" : ObjectId("5cc102fb33907ae66490e46d"),
"name" : "wmyskxz",
"age" : 22
}
{
"_id" : ObjectId("5cc108fb33907ae66490e46e"),
"name" : "wmyskxz-test",
"age" : 22,
"x" : 1,
"y" : 30
}
3.2.3 更新(Update)
MongoDB 提供 update 方法更新文檔:
-
db.collection.updateOne()
更新最多一個符合條件的文檔 -
db.collection.updateMany()
更新所有符合條件的文檔 -
db.collection.replaceOne()
替代最多一個符合條件的文檔 -
db.collection.update()
默認(rèn)更新一個文檔,可配置 multi 參數(shù)羹应,跟新多個文檔
以 update()
方法為例揽碘。其格式:
> db.collection.update(
<query>,
<update>,
{
upsert: <boolean>,
multi: <boolean>
}
)
各參數(shù)意義:
- query 為查詢條件
- update 為修改的文檔
- upsert 為真,查詢?yōu)榭諘r插入文檔
- multi 為真园匹,更新所有符合條件的文檔
下面我們測試把 name
字段為 wmyskxz
的文檔更新一下試試:
> db.newCollection.update({name:"wmyskxz"},{name:"wmyskxz",age:30})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
要注意的是雳刺,如果更新文檔只傳入 age
字段,那么文檔會被更新為{age: 30}
裸违,而不是{name:"wmyskxz", age:30}
掖桦。要避免文檔被覆蓋,需要用到 $set
指令供汛,$set
僅替換或添加指定字段:
> db.newCollection.update({name:"wmyskxz"},{$set:{age:30}})
如果要在查詢的文檔不存在的時候插入文檔枪汪,要把 upsert 參數(shù)設(shè)置真值:
> db.newCollection.update({name:"wmyskxz11"},{$set:{age:30}},{upsert:true})
update 方法默認(rèn)情況只更新一個文檔涌穆,如果要更新符合條件的所有文檔,要把 multi 設(shè)為真值雀久,并使用 $set
指令:
> db.newCollection.update({age:{$gt:20}},{$set:{test:"A"}},{multi:true})
WriteResult({ "nMatched" : 3, "nUpserted" : 0, "nModified" : 3 })
> db.newCollection.find()
{ "_id" : ObjectId("5cc1026533907ae66490e46c"), "x" : 1 }
{ "_id" : ObjectId("5cc102fb33907ae66490e46d"), "name" : "wmyskxz", "age" : 30, "test" : "A" }
{ "_id" : ObjectId("5cc108fb33907ae66490e46e"), "name" : "wmyskxz-test", "age" : 22, "x" : 1, "y" : 30, "test" : "A" }
{ "_id" : ObjectId("5cc110148d0a578f03d43e81"), "name" : "wmyskxz11", "age" : 30, "test" : "A" }
3.2.4 刪除(Delete)
MongoDB 提供了 delete 方法刪除文檔:
-
db.collection.deleteOne()
刪除最多一個符合條件的文檔 -
db.collection.deleteMany()
刪除所有符合條件的文檔 -
db.collection.remove()
刪除一個或多個文檔
以 remove 方法為例:
> db.newCollection.remove({name:"wmyskxz11"})
> db.newCollection.remove({age:{$gt:20}},{justOne:true})
> db.newCollection.find()
{ "_id" : ObjectId("5cc1026533907ae66490e46c"), "x" : 1 }
{ "_id" : ObjectId("5cc108fb33907ae66490e46e"), "name" : "wmyskxz-test", "age" : 22, "x" : 1, "y" : 30, "test" : "A" }
MongoDB 提供了 drop 方法刪除集合宿稀,返回 true 表面刪除集合成功:
> db.newCollection.drop()
3.2.5 小結(jié)
相比傳統(tǒng)關(guān)系型數(shù)據(jù)庫,MongoDB 的 CURD 操作更像是編寫程序赖捌,更符合開發(fā)人員的直覺原叮,不過 MongoDB 同樣也支持 SQL 語言。MongoDB 的 CURD 引擎配合索引技術(shù)巡蘸、數(shù)據(jù)聚合技術(shù)和 JavaScript 引擎奋隶,賦予 MongoDB 用戶更強大的操縱數(shù)據(jù)的能力。
參考文章:簡明 MongoDB 入門教程 - https://segmentfault.com/a/1190000010556670
4 MongoDB 數(shù)據(jù)模型的一些討論
前置申明:這一部分基于以下鏈接整理 https://github.com/justinyhuang/the-little-mongodb-book-cn/blob/master/mongodb.md#%E8%AE%B8%E5%8F%AF%E8%AF%81
這是一個抽象的話題悦荒,與大多數(shù)NoSQL方案相比,在建模方面,面向文檔的數(shù)據(jù)庫算是和關(guān)系數(shù)據(jù)庫相差最小的唯欣。這些差別是很小,但是并不是說不重要搬味。
4.1 沒有連接(Join)
您要接受的第一個也是最基本的一個差別境氢,就是 MongoDB 沒有連接(join)。我不知道MongoDB不支持某些類型連接句法的具體原因碰纬,但是我知道一般而言人們認(rèn)為連接是不可擴展的萍聊。也就是說,一旦開始橫向分割數(shù)據(jù)悦析,最終不可避免的就是在客戶端(應(yīng)用程序服務(wù)器)使用連接寿桨。且不論MongoDB為什么不支持連接,事實是數(shù)據(jù)是有關(guān)系的强戴,可是MongoDB不支持連接亭螟。(譯者:這里的關(guān)系指的是不同的數(shù)據(jù)之間是有關(guān)聯(lián)的,對于沒有關(guān)系的數(shù)據(jù)骑歹,就完全不需要連接预烙。)
為了在沒有連接的MongoDB中生存下去,在沒有其他幫助的情況下道媚,我們必須在自己的應(yīng)用程序中實現(xiàn)連接扁掸。
基本上我們需要用第二次查詢去找到相關(guān)的數(shù)據(jù)。找到并組織這些數(shù)據(jù)相當(dāng)于在關(guān)系數(shù)據(jù)庫中聲明一個外來的鍵∽钣颍現(xiàn)在先別管什么獨角獸了谴分,我們來看看我們的員工。首先我們創(chuàng)建一個員工的數(shù)據(jù)(這次我告訴您具體的_id值羡宙,這樣我們的例子就是一樣的了):
db.employees.insert({_id: ObjectId("4d85c7039ab0fd70a117d730"), name: 'Leto'})
然后我們再加入幾個員工并把 Leto
設(shè)成他們的老板:
db.employees.insert({_id: ObjectId("4d85c7039ab0fd70a117d731"), name: 'Duncan', manager: ObjectId("4d85c7039ab0fd70a117d730")});
db.employees.insert({_id: ObjectId("4d85c7039ab0fd70a117d732"), name: 'Moneo', manager: ObjectId("4d85c7039ab0fd70a117d730")});
(有必要再強調(diào)一下狸剃,_id可以是任何的唯一的值掐隐。在實際工作中你很可能會用到ObjectId狗热, 所以我們在這里也使用它)
顯然钞馁,要找到Leto的所有員工,只要執(zhí)行:
db.employees.find({manager: ObjectId("4d85c7039ab0fd70a117d730")})
沒什么了不起的匿刮。在最糟糕的情況下僧凰,為彌補連接的缺失需要做的只是再多查詢一次而已,該查詢很可能是經(jīng)過索引了的熟丸。
4.1.1 數(shù)組和嵌入文檔(Embedded Documents)
MongoDB 沒有連接并不意味著它沒有其他的優(yōu)勢训措。還記得我們曾說過 MongoDB 支持?jǐn)?shù)組并把它當(dāng)成文檔中的一級對象嗎?當(dāng)處理多對一或是多對多關(guān)系的時候光羞,這一特性就顯得非常好用了绩鸣。用一個簡單的例子來說明,如果一個員工有兩個經(jīng)理纱兑,我們可以把這個關(guān)系儲存在一個數(shù)組當(dāng)中:
({name: 'Siona', manager: [ObjectId("4d85c7039ab0fd70a117d730"), ObjectId("4d85c7039ab0fd70a117d732")] })
需要注意的是呀闻,在這種情況下,有些文檔中的 manager
可能是一個向量潜慎,而其他的卻是數(shù)組捡多。在兩種情況下,前面的 find
還是一樣可以工作:
db.employees.find({manager: ObjectId("4d85c7039ab0fd70a117d730")})
很快您就會發(fā)現(xiàn)數(shù)組中的值比起多對多的連接表(join-table)來說要更容易處理铐炫。
除了數(shù)組垒手,MongoDB 還支持嵌入文檔。嘗試插入含有內(nèi)嵌文檔的文檔倒信,像這樣:
db.employees.insert({_id: ObjectId("4d85c7039ab0fd70a117d734"), name: 'Ghanima', family: {mother: 'Chani', father: 'Paul', brother: ObjectId("4d85c7039ab0fd70a117d730")}})
也許您會這樣想科贬,確實也可以這樣做:嵌入文檔可以用‘.’符號來查詢:
db.employees.find({'family.mother': 'Chani'})
就這樣,我們簡要地介紹了嵌入文檔適用的場合以及您應(yīng)該怎樣使用它鳖悠。
4.1.2 DBRef
MongoDB 支持一個叫做 DBRef 的功能唆迁,許多 MongoDB 的驅(qū)動都提供對這一功能的支持。當(dāng)驅(qū)動遇到一個 DBRef
時它會把當(dāng)中引用的文檔讀取出來竞穷。DBRef
包含了所引用的文檔的 ID 和所在的集合唐责。它通常專門用于這樣的場合:相同集合中的文檔需要引用另外一個集合中的不同文檔。例如瘾带,文檔 1 的 DBRef
可能指向 managers
中的文檔鼠哥,而文檔 2 中的 DBRef
可能指向 employees
中的文檔。
4.1.3 范規(guī)范化(Denormalization)
代替連接的另一種方法就是反規(guī)范化數(shù)據(jù)看政。在過去朴恳,反規(guī)范化是為性能敏感代碼所設(shè),或者是需要數(shù)據(jù)快照(例如審計日志)的時候才應(yīng)用的允蚣。然而于颖,隨著NoSQL的日漸普及,有許多這樣的數(shù)據(jù)庫并不提供連接操作嚷兔,于是作為規(guī)范建模的一部分森渐,反規(guī)范化就越來越常見了做入。這樣說并不是說您就需要為每個文檔中的每一條信息創(chuàng)建副本。與此相反同衣,與其在設(shè)計的時候被復(fù)制數(shù)據(jù)的擔(dān)憂牽著走竟块,還不如按照不同的信息應(yīng)該歸屬于相應(yīng)的文檔這一思路來對數(shù)據(jù)建模旧巾。
比如說讲婚,假設(shè)您在編寫一個論壇的應(yīng)用程序。把一個 user
和一篇 post
關(guān)聯(lián)起來的傳統(tǒng)方法是在 posts
中加入一個 userid
的列丁眼。這樣的模型中埠况,如果要顯示 posts
就不得不讀人市(連接)users
。一種簡單可行的替代方案就是直接把 name
和 userid
存儲在 post
中辕翰。您甚至可以用嵌入文檔來實現(xiàn)违帆,比如說 user: {id: ObjectId('Something'), name: 'Leto'}
。當(dāng)然金蜀,如果允許用戶更改他們的用戶名刷后,那么每當(dāng)有用戶名修改的時候,您就需要去更新所有的文檔了(這需要一個額外的查詢)渊抄。
對一些人來說改用這種方法并非易事尝胆。甚至在一些情況下根本行不通。不過別不敢去嘗試這種方法:有時候它不僅可行护桦,而且就是正確的方法含衔。
4.1.4 應(yīng)該選擇哪一種?
當(dāng)處理一對多或是多對多問題的時候二庵,采用id數(shù)組往往都是正確的策略贪染。可以這么說催享,DBRef
并不是那么常用杭隙,雖然您完全可以試著采用這項技術(shù)。這使得新手們在面臨選擇嵌入文檔還是手工引用(manual reference)時猶豫不決因妙。
首先痰憎,要知道目前一個單獨的文檔的大小限制是 4MB,雖然已經(jīng)比較大了攀涵。了解了這個限制可以為如何使用文檔提供一些思路铣耘。目前看來多數(shù)的開發(fā)者還是大量地依賴手工引用來維護(hù)數(shù)據(jù)的關(guān)系。嵌入文檔經(jīng)常被使用以故,but mostly for small pieces of data which we want to always pull with the parent document蜗细。一個真實的例子,我把 accounts
文檔嵌入存儲在用戶的文檔中怒详,就像這樣:
db.users.insert({name: 'leto', email: 'leto@dune.gov', account: {allowed_gholas: 5, spice_ration: 10}})
這不是說您就應(yīng)該低估嵌入文檔的作用炉媒,也不是說應(yīng)該把它當(dāng)成是鮮少用到的工具并直接忽略踪区。將數(shù)據(jù)模型直接映射到目標(biāo)對象上可以使問題變得更加簡單,也往往因此而不再需要連接操作橱野。當(dāng)您知道 MongoDB 允許對嵌入文檔的域進(jìn)行查詢并做索引后,這個說法就尤其顯得正確了善玫。
4.2 集合:少一些還是多一些水援?
既然集合不強制使用模式,那么就完全有可能用一個單一的集合以及一個不匹配的文檔構(gòu)建一個系統(tǒng)茅郎。以我所見過的情況蜗元,大部分的 MongoDB 系統(tǒng)都像您在關(guān)系數(shù)據(jù)庫中所見到的那樣布局。換句話說系冗,如果在關(guān)系數(shù)據(jù)庫中會用表奕扣,那么很有可能在 MongoDB 中就要用集合(多對多連接表在這里是一個不可忽視的例外)
當(dāng)把嵌入文檔引進(jìn)來的時候,討論就會變得更加有意思了掌敬。最常見的例子就是博客系統(tǒng)惯豆。是應(yīng)該分別維護(hù) posts
和 comments
兩個集合,還是在每個 post
中嵌入一個 comments
數(shù)組奔害?暫且不考慮那個 4MB 的限制(哈姆雷特所有的評論也不超過200KB楷兽,誰的博客會比他更受歡迎?)华临,大多數(shù)的開發(fā)者還是傾向于把數(shù)據(jù)劃分開芯杀。因為這樣既簡潔又明確。
沒有什么硬性的規(guī)定(呃雅潭,除了 4MB 的限制)揭厚。做了不同的嘗試之后您就可以憑感覺知道怎樣做是對的了。
總結(jié)
至此已經(jīng)對 MongoDB 有了一個基本的了解和入門扶供,但是要運用在實際的項目中仍然有許多實踐需要自己去完成
按照慣例黏一個尾巴:
歡迎轉(zhuǎn)載筛圆,轉(zhuǎn)載請注明出處!
簡書ID:@我沒有三顆心臟
github:wmyskxz
歡迎關(guān)注公眾微信號:wmyskxz
分享自己的學(xué)習(xí) & 學(xué)習(xí)資料 & 生活
想要交流的朋友也可以加qq群:3382693