7.4核心要点:MongoDB更新与删除
7.4.1更新类型与参数选项
多文档更新
默认情况下,只会更新匹配查询器的第一个文档。要更新所有匹配的文档,就需要显示指定多文档更新模式。在shell中,使用multi:true来实现。
在文档级别更新是原子性的,这意味着一条更新10个文档的语句可能在更新3个文档后由于某种原因失败。应用程序必须根据自己的策略来处理这些失败。
upserts
upserts,当文档存在时更新,文档不存在时候插入数据。
如果查询选择器匹配,更新正常进行。如果没有匹配的文档,就会插入新的文档。新文档的字段是查询选择器和目标更新文档的逻辑合并。upsert无法与替换更新文档模式一起工作。
7.4.2更新操作符
标准操作符
$inc:可以使用$inc操作符来增加或者减少指定数值(可以是1,-1,2.78)。
$set和$unset:
如果要设置某个文档的特定的key值,就可以用$set。如果key键已经存在,它的值就会被重写;否则,创建新的key值。
$unset会从文档中删除提供的key。不过在单个数组元素上使用$unset仅仅会设置元素的值为null而不是删除元素。要完全删除数组元素,可以使用$pull、$pop。

$rename:修改key的名字。可以使用$rename。

$SetOnInsert:在upsert中,$setOnInsert只会在insert的时候起作用,修改的时候不起作用。

数组更新操作符
$push、$pushAll、$each($each只能和$push、$addToSet一起使用)
$push在数组后面追加值(可以往数组中添加任意类型的值)。
如果要在一次更新里添加多个更新,可以组合使用$each和$push。

$slice:允许用来剪短数组的大小、删除旧的值。
示例:


执行后,temps数组的值

在推送数据到数组后,从开头删除值,只留下4个数值。如果传递-1给$slice操作符,结果数组是[96]。如果传递0,结果就是[]。如果传递正整数,会从数组尾部开始删除元素。比如使用了$slice:4,那么结果是
$sort:排序
$addToSet:往数组后面添加值。但是它只会添加数组中不存在的值。
($each只能和$push、$addToSet一起使用)
$pop:从数组中删除元素。可以传1删除最后一直值。可以传-1删除第一个值。


$pull和$pullall
$pull是$pop的复查形势。使用$pull,可以通过值精确指定要删除的元素。

$pullall与$pull的工作类似,允许提供要删除值的列表。而且可以通过传递查询条件来删除。

定位更新(位置操作符$)
定位操作符允许我们通过原点选择器来定位要更新的元素。
例如:

假如我们想设置第二行项目的数值为5,选择器可以使用sku=10027,但是我们不知道是否存在,或者它在line_items中的下标是多少。我们可以使用位置操作符$来替换下标。

7.4.3findAndMofidy命令
findAndModify(query,update,remove,new,sort,fields,upsert)函数
- query:查询选择器,默认为{}。
- update:指定更新的文档,默认为{}。
- remove:布尔值,如果为true,则返回删除的对象。默认为false。
- new:布尔值,如果为true,这返回修改后的文档。默认为false,返回修改前的文档
- sort:指定排序的方向。使用findAndModify一次就修改一个文档。sort参数可以帮助控制要处理那个文档。
- fields:返回部分字段。
- upsert:布尔值,为true时,findAndMofidy作为upsert操作。如果文档不存在,就创建它。
7.4.4删除
remove(selector)
当selector为{}时,删除整个文档。
7.4.5并发、原子性和隔离(隔离运算符$isolated:隔离其他操作,不允许其他操作交叉更新多个文档)
MongoDB锁特性
1)数据库在内存里保留文档的内部映射。对于非RAM的读/写,数据库都会屈服于其他操作,直到文档加入内存。
2)针对写锁。如果某个写操作需要长时间来完成,那么所有其他的读和写操作都会被阻塞。当前的解决方案允许这种长时间运行的操作为其他读/写让路。当一个操作屈服时,它会暂停,并释放锁,后面再重新启动。
尽管对于锁机制进行了优化,但是MongoDB在大量读/写的情况下还是会影响性能。一种好而简单的避免问题的方法就是把高并发的集合保存到单独的数据库里面。
当更新和删除文档时,这种退让机制可谓是喜忧参半。假设我们想在其他操作发生前更新或者删除文档。此时我们可以使用专门的参数$isolated来保持操作独立。不会让路。
注意,如果使用$isolated半路失败了,就不会有显示的回滚操作。部分文档已经更新而其他部分还是原来的值。因为这些操作失败在于非原子性。$isolated无法在分片集合中使用。
7.4.6更新性能注意事项
理解MongoDB如果更新磁盘上的文档可以帮助我们优化性能。首先要理解的事情就是更新发生的程度。理想情况下,更新影响磁盘上BSON文档的最小部分,因为这样是最高效的。
更新磁盘上文档的三种方式
1)第一种更新是最高效的。当只更新文档的单个值并且BSON文档大小不会发送改变时才发生。比如在$inc操作符号中,$inc只增加整数,磁盘上的文件大小不会改变。如果整数代表int,那么在磁盘上只占4个字节,长整数和double类型会占用8个字节。修改这些数值不会改变存储空间的大小,只需要把文档重新写入磁盘即可。
2)更新是修改文档的大小和数据结构。BSON文档表示为字节数组。前4个字节表示文档的大小。因此使用$push操作符修改文档,既增加文档的大小又修改结构。这需要在磁盘上重写整个文档。虽然这样还不是特别低效率,但是值得我们注意。如果有大型文档(假设4M),我们要在此文档里添加数组,那么在服务端会是很大的工作。
3)更新是重写一个文档。如果文档扩大,但是不能满足现在的磁盘空间,则不仅仅需要重写,还需要移动到新的空间里。这种移动操作如果进程发生,则会非常昂贵。MongoDB会通过动态调整集合分配预留空间的填充因子(padding factor)来优化问题。这意味着当在一个集合中有许多的需要文档迁移更新时,内部预留空间的填充因子会增加。填充因子会乘以每个插入文档的大小来获取额外的空间。这也许可以减少未来文档重新迁移的数量。

浙公网安备 33010602011771号