数据更新,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小时的信息。