有坑勿踩(三)——關於數據更新

前言

數據更新,CRUD中的U,對任何數據庫而言都是最基本的操做。看似簡單的更新操做中會藏着哪些坑?今天聊一聊這個話題。javascript

在寫這個系列文章時,我會假設讀者已經對MongoDB有了最基礎的瞭解,所以一些基本名詞和概念就不作過多的解釋,請本身查閱相關資料。java

數據更新方式

以shell爲例,MongoDB的數據更新可使用如下幾種方式:mongodb

  • db.<collection>.update()
  • db.<collection>.updateMany()
  • db.<collection>.updateOne()
  • db.<collection>.save()
  • db.<collection>.findAndModify()

前三種是因爲歷史緣由產生的,實際上:shell

  • updateMany = update + {multi: true}
  • updateOne = update 或 update + {multi: false}

由於update自己的意義不夠清楚,因此3.0之後纔出現了updateManyupdateOne兩個替代方法。這個方法沒多少要說的,惟一要注意的就是,若是用update方法的話,不要忘記操做符($set, $inc等等),否則……
updateManyupdateOne則沒有這個問題,缺了操做符會直接報錯。數據庫

更新操做對比

update三兄弟和findAndModify

不少人的疑問可能都在這裏,它們到底有什麼區別,傻傻分不清楚。
首先參數不同:segmentfault

請閱讀文檔很少贅述。
其次功能不同,
update只是更新操做,而findAndModify能夠在找到結果後選擇執行更新仍是刪除操做。說白了功能上findAndModify=updateOne+removeOne。注意它只能對單個文檔進行操做。
不管更新仍是刪除,(『找到』『更新』)或(『找到』『刪除』)都是原子性的,這點findAndModifyupdateOne/removeOne沒有任何區別。區別只在於findAndModify在完成動做以後還能夠選擇把更新/刪除以前或以後的文檔返回給你。若是沒有這個操做,那就必須先findupdate或者先updatefind,不管怎麼作,都不能保證中間不被其餘操做捷足先登。所以findAndModify在某些場景下是必要的,好比使用$inc生成遞增序列(注意生成遞增序列作ID不是個好想法,我在這個問題中作過解釋)
由於findAndModify只針對單個文檔,那麼若是條件能找到多個文檔怎麼辦?sort就用在這種場景下。code

update和save

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小時的信息。

相關文章
相關標籤/搜索