數據更新,CRUD中的U,對任何數據庫而言都是最基本的操做。看似簡單的更新操做中會藏着哪些坑?今天聊一聊這個話題。javascript
在寫這個系列文章時,我會假設讀者已經對MongoDB有了最基礎的瞭解,所以一些基本名詞和概念就不作過多的解釋,請本身查閱相關資料。java
以shell爲例,MongoDB的數據更新可使用如下幾種方式:mongodb
db.<collection>.update()
db.<collection>.updateMany()
db.<collection>.updateOne()
db.<collection>.save()
db.<collection>.findAndModify()
前三種是因爲歷史緣由產生的,實際上:shell
由於update
自己的意義不夠清楚,因此3.0之後纔出現了updateMany
和updateOne
兩個替代方法。這個方法沒多少要說的,惟一要注意的就是,若是用update
方法的話,不要忘記操做符($set
, $inc
等等),否則……updateMany
和updateOne
則沒有這個問題,缺了操做符會直接報錯。數據庫
不少人的疑問可能都在這裏,它們到底有什麼區別,傻傻分不清楚。
首先參數不同:segmentfault
請閱讀文檔很少贅述。
其次功能不同,update
只是更新操做,而findAndModify
能夠在找到結果後選擇執行更新仍是刪除操做。說白了功能上findAndModify
=updateOne
+removeOne
。注意它只能對單個文檔進行操做。
不管更新仍是刪除,(『找到』『更新』)或(『找到』『刪除』)都是原子性的,這點findAndModify
和updateOne
/removeOne
沒有任何區別。區別只在於findAndModify
在完成動做以後還能夠選擇把更新/刪除以前或以後的文檔返回給你。若是沒有這個操做,那就必須先find
再update
或者先update
再find
,不管怎麼作,都不能保證中間不被其餘操做捷足先登。所以findAndModify
在某些場景下是必要的,好比使用$inc
生成遞增序列(注意生成遞增序列作ID不是個好想法,我在這個問題中作過解釋)
由於findAndModify
只針對單個文檔,那麼若是條件能找到多個文檔怎麼辦?sort
就用在這種場景下。code
save
其實是一種特殊的update
,即不帶操做符的update
。通俗地說叫『替換』。替換,表明你已經有這個文檔完整的樣子,即表明你已經把整個文檔從數據庫中讀出來,在內存中進行了修改,而後完整替換回去。你並不能保證數據在被你讀出來到寫回去期間是否有別人已經改了數據庫中的記錄,這就是第一個風險,save
操做存在潛在的可能性會覆蓋掉別人更新過的數據。例如:ip
db.celebrity.findOne() { _id: "孫悟空", title: "石猴" age: 500 }
你執行了:內存
var obj = db.celebrity.findOne({_id: "孫悟空"}); obj.title = "弼馬溫"; // 其餘操做 db.celebrity.save(obj);
在『其餘操做』的地方有人把孫悟空的title
更新成了『齊天大聖』,很顯然在你save
的時候你會把它改回『弼馬溫』。rem
除了上述問題,save
還帶來一個額外的反作用,由於整個文檔都保存進去了,意味着整個文檔都會進入oplog,這會顯著增長oplog的使用速度。所以過分使用save
經常還會形成oplog不夠用,須要很大的oplog才能足夠保存24小時的信息。