Overview
MongoDB为客户端提供批量执行写入操作的能力。从MongoDB 8.0 开始,您可以跨多个数据库和集合执行批量写入操作。如果您使用的是MongoDB 8.0 之前的版本,则可以对单个集合批量写入操作。
要跨 MongoDB 8.0 中的多个数据库和集合执行批量写入操作,请使用 bulkWrite 数据库命令或 Mongo.bulkWrite() mongosh 方法。
要对单个集合执行批量写入操作,请使用 db.collection.bulkWrite() mongosh 方法。如果运行的是MongoDB 8.0 或更高版本,还可以使用 bulkWrite 或 Mongo.bulkWrite() 写入单个集合。
有序操作与无序操作
您可以将批量写入操作设立为有序或无序。
对于有序的操作列表, MongoDB以串行方式执行操作。如果在处理其中一个写入操作期间发生错误, MongoDB将返回而不处理列表中任何剩余的写入操作。
对于操作的无序列表, MongoDB可以并行执行操作,但不保证一定会执行此行为。如果在处理其中一个写入操作期间发生错误, MongoDB将继续进程列表中剩余的写入操作。
在分片集合上执行操作的有序列表通常比执行无序列表慢,因为对于有序列表,每个操作都必须等待前一个操作完成。
默认下,所有批量写入命令和方法都执行有序操作。要指定无序操作,请在调用首选命令或方法时将 ordered 选项设立为 false。要学习;了解有关每个命令或方法的语法的更多信息,请参阅上面链接的相应页面。
批量写入方法
所有批量写入方法和命令都支持以下写入操作:
insertOne
updateOne
UpdateMany
replaceOne
deleteOne
deleteMany
当您调用首选命令或方法时,您将每个写入操作作为大量中的文档传递。要学习;了解有关每个命令或方法的语法的更多信息,请参阅上面链接的相应页面。
例子
本页上的示例使用 sample_mflix示例数据集的数据。有关如何将此数据集加载到自管理MongoDB 部署中的详细信息,请参阅加载示例数据集。如果对示例数据库进行了任何修改,则可能需要删除并重新创建数据库才能运行本页上的示例。
db.collection.bulkWrite()
db.collection.bulkWrite()示例对users 集合运行以下操作:
使用
insertOne添加两个文档。使用
updateOne更新一个文档。使用
deleteOne删除文档。使用
replaceOne替换一个文档。
db.users.bulkWrite( [ { insertOne: { document: { _id: ObjectId("67a1b2c3d4e5f6a7b8c9d0e2"), name: "Daenerys Targaryen", email: "daenerys.t@example.com", password: "$2b$12$UREFwsRUoyF0CRqGNK0LzO0HM/jLhgUCNNIJ9RJAqMUQ74crlJ1Vu" } } }, { insertOne: { document: { _id: ObjectId("67a1b2c3d4e5f6a7b8c9d0e3"), name: "Jaime Lannister", email: "jaime.l@example.com", password: "$2b$12$UREFwsRUoyF0CRqGNK0LzO0HM/jLhgUCNNIJ9RJAqMUQ74crlJ1Vu" } } }, { updateOne: { filter: { name: "Ned Stark" }, update: { $set: { email: "ned.stark.updated@example.com" } } } }, { deleteOne: { filter: { name: "Daenerys Targaryen" } } }, { replaceOne: { filter: { name: "Jaime Lannister" }, replacement: { name: "Jaime Lannister", email: "jaime.lannister.new@example.com", password: "$2b$12$NewPasswordHashHere123456789012345678901234567890" } } } ] )
输出示例,包括已完成操作的摘要:
{ acknowledged: true, insertedCount: 2, insertedIds: { '0': ObjectId("67a1b2c3d4e5f6a7b8c9d0e2"), '1': ObjectId("67a1b2c3d4e5f6a7b8c9d0e3") }, matchedCount: 2, modifiedCount: 2, deletedCount: 1, upsertedCount: 0, upsertedIds: {} }
有关更多示例,请参阅 db。集合.bulkWrite() 示例。
Mongo.bulkWrite()
此示例使用Mongo.bulkWrite() 在 sample_mflix数据库上按顺序执行以下操作。
将文档插入到
users集合中更新
theaters集合中的文档将另一个文档插入到
users集合中
db.getMongo().bulkWrite( [ { namespace: 'sample_mflix.users', name: 'insertOne', document: { name: 'Cersei Lannister', email: 'cersei.l@example.com', password: '$2b$12$UREFwsRUoyF0CRqGNK0LzO0HM/jLhgUCNNIJ9RJAqMUQ74crlJ1Vu' } }, { namespace: 'sample_mflix.theaters', name: 'updateOne', filter: { theaterId: 1000 }, update: { $set: { "location.address.street1": "350 W Market", "location.address.city": "Bloomington" } } }, { namespace: 'sample_mflix.users', name: 'insertOne', document: { name: 'Sansa Stark', email: 'sansa.s@example.com', password: '$2b$12$UREFwsRUoyF0CRqGNK0LzO0HM/jLhgUCNNIJ9RJAqMUQ74crlJ1Vu' } } ], { ordered: true, bypassDocumentValidation: true, verboseResults: true } )
mongosh 按顺序执行批量写入并返回以下文档:
{ acknowledged: true, insertedCount: 2, matchedCount: 1, modifiedCount: 1, deletedCount: 0, upsertedCount: 0, insertResults: Map(2) { 0 => { insertedId: "..." }, 2 => { insertedId: "..." } }, updateResults: Map(1) { 1 => { matchedCount: 1, modifiedCount: 1, didUpsert: false } }, deleteResults: Map(0) {} }
批量插入分片集合的策略
大批量插入操作(包括初始数据插入或例行数据导入)可能影响分片集群的性能。对于批量插入,请考虑以下策略:
预拆分集合
如果您的分片集合为空,且分片键的第一个键没有使用哈希分片,则您的集合只有一个初始数据块,而它位于单个分片上。然后,MongoDB 必须花费一些时间来接收数据并将数据块分发到可用的分片。为避免出现此性能费用,可通过在分片集群中创建范围来预先分割此集合。
无序写入到 mongos
要提高分片的片集群的写入性能,请在调用首选方法或命令时将 ordered 设置为 false,以执行无序批量写入。mongos 可以尝试同时向多个分片发送写入内容。对于空集合,首先按照分片集群中的分割数据段中的描述预分割集合。
避免单调限速
如果分片键在插入期间单调增加,则所有已插入数据都会进入集合中的最后一个数据段,该数据段将始终出现在单个分片上。因此,集群的插入容量永远不会超过该单个分片的插入容量。
如果插入量大于单个分片可以处理的容量,并且无法避免分片键的单调增加,则可以考虑对应用程序进行以下修改:
反转分片键的二进制位。这样将保留信息,并避免将插入顺序与递增的值序列相关联。
交换第一个和最后一个 16 位字,“随机打乱”插入。
例子
以下示例(采用 C++ 编写)交换生成的 BSON ObjectId 的前导和尾随 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 }