Mongodb Manual閱讀筆記:CH2 Mongodb CRUD 操做
Mongodb Manual閱讀筆記:CH3 數據模型(Data Models)
Mongodb Manual閱讀筆記:CH4 管理
Mongodb Manual閱讀筆記:CH5 安全性
Mongodb Manual閱讀筆記:CH6 聚合
Mongodb Manual閱讀筆記:CH7 索引
Mongodb Manual閱讀筆記:CH8 複製集
Mongodb Manual閱讀筆記:CH9 Shardinghtml
mongodb提供了建立,讀取,修改,刪除簡稱CRUDsql
2 Mongodb CRUD 操做mongodb
2.1 Mongodb CRUD簡介shell
2.1.1數據庫操做數據庫
2.1.1.1查詢json
mongodb存儲的文檔是和json格式相似的,可是mongodb存儲的是BSON也就是,以2進制方式保存JSON格式數據。在mongodb中文檔至關於記錄,而存文檔的Collection至關於表。
Mongodb能夠對一個collection經過條件來過濾文檔,而且能夠用projection選擇輸出
建立,更新,刪除數據,只能修改單個collection(不包含嵌入方式)
Mongodb能夠順序的表示,也能夠強制惟一性保存,mongodb中索引也是用b樹保存,
讀偏好主要是用在複製集羣中,從哪一個實例中讀取。
不一樣的寫注意保證了不一樣的數據寫入要求,級別越低寫入越快,可是寫入安全性越低
讀操做:遊標,查詢優化,分佈式查詢
寫操做:寫注意,分佈式寫操做
Mongodb經過提供的接口db.collection.find()方法來查詢數據,會返回一個遊標
查詢行爲
1.全部的查詢只能針對一個collection
2.能夠經過limits,skips,sort來影響查詢結果
3.用sort()來範圍查詢的順序
4.update修改數據和select的條件語法同樣
5.在aggregation(聚合)管道中,$match管道提供了對mongodb的訪問
Projection用來限制輸出那些字段,_id是默認輸出的。
Projection行爲
1._id會默認出現,除非顯示設置
2.對於包含array的字段,提供了$elemMatch,$slice,$等操做符
3.對於聚合,使用$Project來處理
mongodb查詢db.collection.find()返回一個遊標,若沒有分配給一個變量直接運行20次
遊標行爲對於遊標默認不活動時間超過10分鐘就會被銷燬,固然能夠指定noTimeout標記,來先限制等待。
遊標隔離級別遊標在有效期內是不隔離的,如有寫入操做就會影響,可使用快照模式來處理
遊標Batch數據從mongodb到客戶端是以batch的方式,batch的大小最大不超過最大BSON文檔的大小,對於不少查詢而言,第一個batch返回101個文檔,或者1MB的大小,以後是4MB。
對於包含sort的查詢可是沒有索引,mongodb會導入到內存而後排序,會在第一個batch的時候返回全部的結果。用cursor.hasnext()和cursor.next()的組合來看有沒有下一個batct,獲取下一個batch
遊標信息經過命令cursorInfo來獲取遊標的信息:1.打開的遊標總數,2.當前使用的客戶端遊標大小,3.從服務重啓來,超時的遊標數。
索引和其餘數據庫中的同樣,減小了不必數據的查詢。
建立索引來支持查詢使用db.collection.ensureIndex()來建立索引
索引的選擇度某些查詢操做時選擇度不好,好比$nin,$ne,高選擇度的才能夠更好的使用索引,$regex 也是沒有辦法使用索引的。
覆蓋查詢覆蓋查詢的條件:1.全部的字段都是索引的一部分,2.全部返回的字段也在索引中
Mongodb優化器處理查詢,並根據索引選擇一個最有效的索引,當collection的數據量被改變優化器會偶爾重建計劃,可使用explain()來查看執行計劃。
查詢優化
優化器建立計劃的過程:1.經過不一樣的索引,併發的執行查詢,2.記錄負荷的結果到buffer中,3.當非排序的查詢計劃所有返回,或者順序的執行計劃所有返回,或者順序的執行計劃返回了超過了一個指標,那麼會中止其餘執行的計劃並選擇這個計劃
計劃重建
當發生如下事件,計劃會被重建:1.超過1000個寫入,2.重建索引,3.添加或者刪除索引,4.服務進程重啓
從Sharded集羣中讀,Sharded容許數據分區,並對應用程序透明,查詢會經過路由到達相關的實例中,經過shard key若沒有shard key,查詢會分發到索引的shard實例中取執行。
從複製集羣中讀,在複製集羣中使用讀偏好,來決定從哪一個實例中讀取數據,讀偏好設置有一下幾個好處:1.減小延遲,2.提供讀的帶寬,3.備份做用,4.容災切換。同時也要注意,主庫和分庫之間的延遲,分庫不能表明當前主庫的數據狀態。
和讀同樣寫操做只能用在一個collection上。
經過接口,db.collection.insert()在collection上建立一個文檔,若update接口在有upset的標記下,也能夠實現建立文檔的功能。
插入的行爲
若是你插入時,沒有指定_id那麼mongodb會分配一個,若指定了_id須要保持在collection中是惟一的。
經過接口,db.collection.update()來實現更新操做,也能夠用save
更新行爲
update默認只對一行進行更新,若要對多行進行更新要使用multi選項,通沒有指定相似set的參數,就會把全部影響的文檔替換,當更新是,文檔的大小超過了原先分配的大小,那麼會從新分配空間,而後把文檔放入,這樣會致使,文檔內的字段重排。
刪除經過接口,db.collection.delete()實現。
刪除的行爲
默認使用delete不太條件,會把因此的文檔所有刪除
修改一個文檔,能夠支持原子性,可是對多個文檔的修改就不支持原子性,儘管如此單仍是可使用隔離操做(isolaation_operator)。也能夠經過人工方式實現2階段提交,來保證隔離性。
弱寫注意,可能會致使一些錯誤,由於不須要等服務端返回寫入成功,強寫注意,client能夠等待寫入完成。db.runCommand( { getLastError: 1, w: 2 } )來配置。
寫注意級別:忽略錯誤(Error Ingored),最低級的級別,mongodb不會通知寫入操做完成,包含 發生錯誤,性能最佳,可是會影響寫入數據的一致性和持久性,w:-1
不通知:mongodb不會通知寫入成功或者失敗,可是當發生錯誤是,仍是會盡力接受和處理錯誤,能夠發現由系統網絡配置致使的網絡錯誤。w:0
通知:能夠取回一個寫入操做信息,這個級別,容許客戶端抓取網絡,重複key等錯誤,w:1。mongodb默認使用這個級別,寫注意默認使用無參數的getLastError,對於複製集羣能夠在getLastErrorDefaults中定義默認的寫注意,若沒有定義默認使用getLastError取通知.
日誌:使用這個級別,只有在提交,寫入日誌後,服務纔會通知寫入操做,這樣能夠保證服務崩潰後數據庫的恢復。使用這個操做要指定w:1,j:true
複製通知:使用這個級別能夠保證,寫操做寫入到了複製集羣,w:>1,在複製集羣下,只要再設置j:true,主庫就能夠用日誌寫注意級別。
在sharded集羣中寫:能夠根據shard key來對分區插入數據,能夠提升寫入性能,若沒有key,會被廣播到全部的shard,若數據庫只插入到一個key,那麼可能照成單點性能問題。
在複製集中寫:在複製集中寫入,會被寫入到primary上,而後記錄在oplog或者log中,而後根據oplog分發到分庫中,對於密集的寫入操做會致使分庫和主庫之間嚴重的延遲,雖然能夠經過寫注意保證一致性,可是會形成寫入性能問題
索引:和其餘數據庫同樣,寫操做會修改索引,會帶來一些性能消耗。
文檔增加:update 操做文檔的空間時,會分配一個新空間,而後把文檔複製進去,這樣會加大update的時間,in-place比文檔增加來的有效。
硬件:不少元素和存儲系統有關,影響着服務的性能,如,隨機讀寫性能,磁盤cache,預讀,RAID級別。
日誌:mongodb使用順序寫方式來記錄日誌,日誌提供了一致性和crash彈性,考慮一下方面來增長日誌性能:1.使用獨立的設備,2.若寫注意級別爲日誌,mongodb經過減小提交間隔來減小寫負荷,3.經過手動配置journalCommitInterval來減小提交間隔。
使用insert方法:insert方法經過傳入一個數組來執行大數據量寫入。經過寫注意的設置大數據量寫入,能夠顯著的提升性能。而且經過ContinueOnError選項可讓批量插入有錯誤下還能完成。
在Shard集羣中:ContinueOnError只在非分片中有效,這類大批量寫入,對sharded集羣來講會下降性能若要寫入考慮一下方案:
預分配splits:大批量導入中,若collection是空的,那麼只有一個spilt,當插入時 從新分配split致使性能問題因此要預先split
避免單調增加:可能大批量插入只針對一個sharded key,這樣sharded的性能就沒 有辦法體現,如沒法避免,能夠考慮:1.二進制方式翻轉shard key,2.key的前16位和 後16位互換。
由於超出文檔就會從新分配,爲了不這個問題,能夠事先留白,減小重分配的可能性。
留白因子:可使用db.collection.stats()查看當前的paddingFactor,留白的大小能夠經過一個公式計算:padding size = (paddingfactor-1)*<document size>。留白並不能對每一個文檔作準確的設置,只能根據文檔大小的平均數。留白的大小隻能經過執行compact或者repairDatabase選項回收。
刪除留白:能夠經過compact,repairDatabase和初始化複製同步選項來移除,導出導入也能夠。
行分配策略:爲了更加有效的使用由於刪除而空閒的空間,或者文檔從新分配的空間,能夠指定mongodb分配的行大小,大小爲2的n次。
在mongodb中,可使用insert,update,save來插入數據
向inventory插入數據:db.inventory.insert( { _id: 10, type: "misc", item: "card", qty: 15 } )
使用update插入文檔的時候要指明upsert:true,表示若是有數據則修改,沒數據則插入
db.inventory.update(
{ type: "book", item : "journal" },
{ $set : { qty: 10 } },
{ upsert : true }
)
{ "_id" : ObjectId("51e8636953dbe31d5f34a38a"), "item" : "journal", "qty" : 10, "type" : "book" }
插入的文檔若是沒有指定_id mongodb會自動分配一個_id。
往inventory插入數據db.inventory.save( { type: "book", item: "notebook", qty: 40 } )
{ "_id" : ObjectId("51e866e48737f72b32ae4fbc"), "type" : "book", "item" : "notebook", "qty" : 40 }
在mongodb中,find()返回一個遊標,經過遊標來獲取返回的文檔。
可使用以下來查詢全部的文檔:
db.inventory.find( {} ),db.inventory.find()
若是要在inventory中查詢type爲food或者snacks的:
db.inventory.find( { type: { $in: [ 'food', 'snacks' ] } } )
在mongodb中,只要都好分開就表示and條件
db.inventory.find( { type: 'food', price: { $lt: 9.95 } } )
db.inventory.find(
{ $or: [
{ qty: { $gt: 100 } },
{ price: { $lt: 9.95 } }
]
}
)
db.inventory.find( { type: 'food', $or: [ { qty: { $gt: 100 } },
{ price: { $lt: 9.95 } } ]
} )
當要查詢子文檔的時候,能夠指定子文檔的全部值,也可使用.(點號) 獲取子文檔的字段值。
也就是匹配整個子文檔
db.inventory.find(
{
producer: {
company: 'ABC123',
address: '123 Street'
}
}
)
用點號獲取子文檔的值
db.inventory.find( { 'producer.company': 'ABC123' } )
匹配方式和子文檔相似
db.inventory.find( { tags: [ 'fruit', 'food', 'citrus' ] } )
只要某個元素在數組中都會被匹配
db.inventory.find( { tags: 'fruit' } )
db.inventory.find( { 'tags.0' : 'fruit' } )
使用數組全部匹配子文檔中的字段:db.inventory.find( { 'memos.0.by': 'shipping' } )
db.inventory.find(
{
'memos.memo': 'on time',
'memos.by': 'shipping'
}
)
Projection能夠限制字段的輸出
Find,函數第二個參數就是projection若是不填就是所有返回
db.inventory.find( { type: 'food' }, { item: 1, qty: 1 } )
其中_id字段是默認返回的全部不用指定
db.inventory.find( { type: 'food' }, { item: 1, qty: 1, _id:0 } )
由於_id是默認返回的,因此要設置_id不返回。
db.inventory.find( { type: 'food' }, { type:0 } )
對於數組來講惟一可用的projection操做就是$elemMath和$slice
如find函數沒有賦值給變量,在shell中會直接迭代20次。
經過如下方式能夠直接迭代20次
var myCursor = db.inventory.find( { type: 'food' } );
myCursor
也能夠手動迭代
var myCursor = db.inventory.find( { type: 'food' } );
var myDocument = myCursor.hasNext() ? myCursor.next() : null;
if (myDocument) {
var myItem = myDocument.item;
print(tojson(myItem));
}
還能夠用forEach方法
var myCursor = db.inventory.find( { type: 'food' } );
myCursor.forEach(printjson);
在mongo shell中能夠能夠直接把遊標toArray(),而後直接訪問數組便可。
var myCursor = db.inventory.find( { type: 'food' } );
var documentArray = myCursor.toArray();
var myDocument = documentArray[3];
Explain()遊標方法,這個方法用來分析查詢的效率,肯定如何使用索引。
db.inventory.find( { type: 'food' } ).explain()
結果
{
"cursor" : "BtreeCursor type_1",
"isMultiKey" : false,
"n" : 5,
"nscannedObjects" : 5,
"nscanned" : 5,
"nscannedObjectsAllPlans" : 5,
"nscannedAllPlans" : 5,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 0,
"nChunkSkips" : 0,
"millis" : 0,
"indexBounds" : { "type" : [
[ "food",
"food" ]
] },
"server" : "mongodbo0.example.net:27017"
}
Cursor的值爲btreecursor表示是用了索引
n表示返回的文檔數
若是要比較索引之間的性能可使用hint來強制
db.inventory.find( { type: 'food' } ).hint( { type: 1 } ).explain()
db.inventory.find( { type: 'food' } ).hint( { type: 1, name: 1 } ).explain()
修改文檔的操做有,update 和save,update是修改文檔,save若是文檔存在則替換文檔。
要修改多個文檔時,要把multi-true設置上。
db.inventory.update(
{ type : "book" },
{ $inc : { qty : -1 } },
{ multi: true }
)
Save能夠替換已經存在的文檔
db.inventory.save(
{
_id: 10,
type: "misc",
item: "placard"
}
)
刪除文檔通常使用remove方法
直接寫不帶參數的remove就會把collection中的文檔所有刪除。
db.inventory.remove()
db.inventory.remove( { type : "food" } )
db.inventory.remove( { type : "food" }, 1 )
當處理多文檔的更新和事務,可使用二階段提交的方式來寫多個文檔
單文檔操做是原子的,多文檔沒有,因此多文檔的操做不能有原子性保障,當執行事務時就可能發生如下問題:
1.原子性:若是一個操做失敗,事務必須回滾到以前的狀態
2.隔離性:同步發生的事務,在事務過程當中必須看到一致性的數據
3.一致性:若是發生數據庫錯誤,中斷了事務,數據庫必須可以恢復到一致的狀態
多文檔的寫可使用二階段提交
假設要作轉帳操做,咱們有一個accounts collection和一個transactions collection用來保存用戶數據和事務數據。
db.accounts.save({name: "A", balance: 1000, pendingTransactions: []})
db.accounts.save({name: "B", balance: 1000, pendingTransactions: []})
設置事務初始化狀態:在建立的 transactions中插入一條數據而且狀態爲initial。
db.transactions.save({source: "A", destination: "B", value: 100, state: "initial"})
而後把事務狀態設置爲pending:在要處理事務以前把事務狀態設置爲pending表示處理這個事務。db.transactions.update({_id: t._id}, {$set: {state: "pending"}})
應用事務到2個帳號上:
db.accounts.update({name: t.source, pendingTransactions: {$ne: t._id}}, {$inc: {balance: -t.value}, $push: {pendingTransactions: t._id}})
db.accounts.update({name: t.destination, pendingTransactions: {$ne: t._id}}, {$inc: {balance: t.value}, $push: {pendingTransactions: t._id}})
更新account的pendingTransaction中沒有t._id的事務,就是t._id不是已經在處理事務,而後設置值,並把設置上pendingTransaction。
設置事務提交狀態:db.transactions.update({_id: t._id}, {$set: {state: "committed"}})
刪除pending事務:
db.accounts.update({name: t.source}, {$pull: {pendingTransactions: t._id}})
db.accounts.update({name: t.destination}, {$pull: {pendingTransactions: t._id}})
設置事務完成:
db.transactions.update({_id: t._id}, {$set: {state: "done"}})
可能會出現錯誤的場景:
1.在初始化事務,應用事務到2個帳號上之間出錯,爲了恢復應用程序應該獲取pending狀態的事務,而後繼續處理。
2.發生在應用事務,事務完成之間的錯誤,爲了恢復應用程序應該獲取done狀態的事務,而後繼續處理。
回滾:某些場景就須要回滾操做
1.當應用事務以後,已經提交了事務,就不該該去回滾事務,而是從新建立一個新的事務,來交換數據
2.在建立事務後,可是沒有應用事務,可使用一下步驟:
設置事務狀態爲取消:db.transactions.update({_id: t._id}, {$set: {state: "canceling"}})
Undo事務:
db.accounts.update({name: t.source, pendingTransactions: t._id}, {$inc: {balance: t.value}, $pull: {pendingTransactions: t._id}})
db.accounts.update({name: t.destination, pendingTransactions: t._id}, {$inc: {balance: -t.value}, $pull: {pendingTransactions: t._id}})
把事務狀態修改成已取消:db.transactions.update({_id: t._id}, {$set: {state: "canceled"}})
多應用程序:多應用程序的時候很容易形成一致性問題和衝突。在這種狀況下,在transaction中加入application字段,來區別應用程序。而且使用findAndModify()
t = db.transactions.findAndModify({query: {state: "initial", application: {$exists: 0}},
update: {$set: {state: "pending", application: "A1"}},
new: true})
在例子中,咱們假設了一些東西好比:
1.老是能夠回滾一個用戶
2.帳號的剩餘能夠爲負數
可是在生產環境下須要考慮:
1.在把事務設置爲pending的時候,須要查看帳號是否有足夠的資金。
2.當事務設置提交時,須要修改借貸方
固然這些操做時在一個文檔裏面的,是原子的
也須要考慮:
1.數據庫接口的寫注意(wirte concern)
2.mongod的journaling是否開啓確保數據庫非乾淨關閉後數據庫狀態的恢復。
默認遊標使用完以後就會被關閉,可是Tailable Cursor不會,當文檔有插入,Tailable Cursor也能夠取到。對於高寫入的capped collection上使用tailable cursor太昂貴。
Tailable cursor有一下特色:
1.遊標不會使用索引,以天然順序返回
2.tailable cursor在初始化階段比較昂貴,新增的文檔代價並非很大。
3.某些狀況下tailable curos會不可用,沒有返回的文檔,返回了collection的末尾,而後被應用程序刪掉了。
看手冊p75
看手冊p76
本節介紹2個建立自增的方法,計數collection,樂觀循環(optimistic loop)
使用一個獨立的計數Collection來跟蹤最新的順序,_id表示順序的名,seq表示最新的順序。
1.插入一個userid的初始化值到計數collection中
db.counters.insert(
{
_id: "userid",
seq: 0
}
)
2.建立一個getNextSequence函數來獲取一個名字的下一個順序值。
function getNextSequence(name) {
var ret = db.counters.findAndModify(
{
query: { _id: name },
update: { $inc: { seq: 1 } },
new: true
}
);
return ret.seq;
}
3.在插入時使用getNextSequence函數
db.users.insert(
{
_id: getNextSequence("userid"),
name: "Sarah C."
}
)
讀者以爲很奇葩的方法,手冊 p80。
有students collection有一個文檔
{
_id: 1,
scores: [
{ attempt: 1, score: 10 },
{ attempt: 2 , score:8 }
]
}
而後再$push裏面使用$each和$sort來實現
db.students.update(
{ _id: 1 },
{ $push: { scores: { $each : [
{ attempt: 3, score: 7 },
{ attempt: 4, score: 4 }
],
$sort: { score: 1 },
$slice: -3
}
}
}
)
輸出結果
{
"_id" : 1,
"scores" : [
{ "attempt" : 3, "score" : 7 },
{ "attempt" : 2, "score" : 8 },
{ "attempt" : 1, "score" : 10 }
]
}
Cursor.count:返回文檔的個數
Cursor.explain:顯示查詢的執行計劃
Cursor.hint:強制mongo使用指定的索引
Cursor.limit:顯示遊標返回文檔個數
Cursor.next:獲取遊標中下一個文檔
Cursor.skip:跳過給定數量的文檔
Cursor.sort:對指定字段排序
Cursor.toArray:遊標返回成數組
Db.collection.count:返回collection文檔個數
Db.collection.distinct:以數組方式返回指定字段的不一樣值
Db.collection.find:在collection執行find返回遊標
Db.collection.findOne:返回一個文檔
Db.collection.insert:在collection上建立文檔
Db.collection.remove:刪除collection上的文檔
Db.collection.save:能夠insert,也能夠替換已經存在的文檔
Db.collection.update:修改已經存在的文檔
主要介紹,寫注意(write concern),sql 到 mongodb的對照表,mongodb的驅動和客戶端lib。
Write concern,mongodb爲寫入提供的保障,可是是經過消耗性能來保證write的安全性。能夠對數據的重要性不一樣分級。
提供write concern,寫入操做以後,驅動使用getLastError來獲取最後才作的信息。返回一個err字段,:
1.null表示寫入成功
2.非null就表示具體的錯誤
GetLastErroe的參數:
J或者journal選項:指定這個選項後,mongodb會寫入日誌到磁盤的journal上確保mongodb忽然關閉以後數據不會丟失。
以下:db.runCommand( { getLastError: 1, j: "true" } )
若是在mongod中並無啓用journal,而後getLastError指定了 j:true會返回一個jnote字段包含了這個文檔。
W選項:這個選項能夠停用write concern,也能夠指定複製集的write concern。可選值:
-1:關閉write operation通知
0:關閉基本的寫入操做通知,可是返回socket異常和網絡異常
1:提供通知,說明已經寫入到了單個實例,或者複製集的primary
>1:說明要寫入到>1個實例中。通常用戶複製集,若是這個值大於複製集成員,mongodb會一致等下去。
Majority:確認寫入操做已經傳播到大多數的已配置複製集
Tag設置:使用tag設置能夠很好的控制複製集成員很好的控制的不一樣write concern。
getLastError還提供了一個超時,若是給0會一直等待。
手冊p85
手冊p95