英文原文地址:https://docs.mongodb.com/v3.2/core/bulk-write-operations/。
本文章属个人翻译,作个人学习之用,如有雷同,纯属巧合。如有错误之处,欢迎指正。
批量写操作
概览
MongoDB给客户端提供了执行批量写操作的功能。批量写只影响单个集合。MongoDB允许应用决定批量写操作可接受的确认级别。
版本3.2新增。
db.collection.bulkWrite()
方法提供了执行批量插入、更新、删除操作的功能。MongoDB也支出通过db.collection.insertMany()
进行批量插入。
有序操作与无序操作
批量写操作可以有序,也可以无序。
对于有序操作,MongoDB串行执行操作。如果在处理其中一个写操作时出现了错误,MongoDB将不会处理剩下的操作,直接返回。见有序批量写。
对于无序操作,MongoDB并行执行操作,但是这个行为没法保证。如果在其中一个写操作时出现了错误,MongoDB会继续处理剩下的写操作。见无序批量写。
在一个分片的集合上执行有序操作一般要比执行无序操作更慢,因为有序操作中,每个操作必须等待前面的操作完成。
bulkWrite()
默认执行有序操作。要指定无序写操作,在选项文档中设置ordered:false
。
bulkWrite()方法
bulkWrite()
支持以下写操作:
insertOne()
updateOne()
updateMany()
replaceOne()
deleteOne()
deleteMany()
每个写操作作为一个文档数组传给bulkWrite()
。
例如,以下例子执行多个写操作:
characters
集合包含以下文档:
{ "_id" : 1, "char" : "Brisbane", "class" : "monk", "lvl" : 4 },
{ "_id" : 2, "char" : "Eldon", "class" : "alchemist", "lvl" : 3 },
{ "_id" : 3, "char" : "Meldane", "class" : "ranger", "lvl" : 3 }
以下`bulkWrite()在集合上执行多个写操作:
try {
db.characters.bulkWrite(
[
{ insertOne :
{
"document" :
{
"_id" : 4, "char" : "Dithras", "class" : "barbarian", "lvl" : 4
}
}
},
{ insertOne :
{
"document" :
{
"_id" : 5, "char" : "Taeln", "class" : "fighter", "lvl" : 3
}
}
},
{ updateOne :
{
"filter" : { "char" : "Eldon" },
"update" : { $set : { "status" : "Critical Injury" } }
}
},
{ deleteOne :
{ "filter" : { "char" : "Brisbane"} }
},
{ replaceOne :
{
"filter" : { "char" : "Meldane" },
"replacement" : { "char" : "Tanys", "class" : "oracle", "lvl" : 4 }
}
}
]
);
}
catch (e) {
print(e);
}
以上操作返回如下:
{
"acknowledged" : true,
"deletedCount" : 1,
"insertedCount" : 2,
"matchedCount" : 2,
"upsertedCount" : 0,
"insertedIds" : {
"0" : 4,
"1" : 5
},
"upsertedIds" : {
}
}
批量插入到分片集合中的策略
大量的批量插入操作,包括初始数据插入或者常规的数据导入,会影响分片集群性能。对于批量插入,需要考虑以下几个策略:
预分割集合
如果分片集合是空的,这个集合只有一个初始块(chunk),留在单个分片上。MongoDB必须花时间来接收数据、创建分块,然后把分块分发给可用的各个分片。要避免这种性能损失,可用预先分割集合,见在分片集群中分块中的描述。
无序写到mongos
要改善分片集群的写操作,用把可选参数ordered
设为fasle的
bulkWrite()。mongos
可以同时把写操作发给多个分片。对于空集合,可以先预分割集合,见在分片集群中分块中的描述。
避免单调瓶颈
如果你的分片键在插入过程中单调递增,所有插入的数据都会跑到集合的最优一个块(chunk),结果会被分配到单个分片上。因此,集群的插入容量不会超过单个分片的插入容量。
如果插入容量比单个分片能处理的更大,而且你又无法避免单调递增的分片键,可以在你的应用中考虑做以下修改:
- 把分片键中的二进制位倒过来。这样既保留了信息,又避免了插入顺序和分片键的增加顺序相关联
- 交换第一个和最后一个16位,把插入“随机化”
例子:
在以下C++例子中,把BSON ObjectId的前16位和后16位进行交换,它们就不会单调递增了。
using namespace mongo; OID make_an_id() { OID x = OID::gen(); const unsigned char *p = x.getData(); swap( (unsigned short&) p[0], (unsigned short&) p[10] ); return x; } void foo() { // create an object BSONObj o = BSON( "_id" << make_an_id() << "x" << 3 << "name" << "jane" ); // now we may insert o into a sharded collection }