In MongoDB, an operation on a single document is atomic. Because you can use embedded documents and arrays to capture relationships between data in a single document structure instead of normalizing across multiple documents and collections, this single-document atomicity obviates the need for distributed transactions for many practical use cases.在MongoDB中,对单个文档的操作是原子性的。因为您可以使用嵌入式文档和数组来捕获单个文档结构中数据之间的关系,而不是跨多个文档和集合进行规范化,所以这种单一文档原子性消除了许多实际用例对分布式事务的需求。
For situations that require atomicity of reads and writes to multiple documents (in a single or multiple collections), MongoDB supports distributed transactions. With distributed transactions, transactions can be used across multiple operations, collections, databases, documents, and shards.对于需要对多个文档(在单个或多个集合中)进行读写原子性的情况,MongoDB支持分布式事务。通过分布式事务,事务可以跨多个操作、集合、数据库、文档和分片使用。
The information on this page applies to deployments hosted in the following environments:此页面上的信息适用于在以下环境中托管的部署:
- MongoDB Atlas
: The fully managed service for MongoDB deployments in the cloud:云中MongoDB部署的完全托管服务
- MongoDB Enterprise
: The subscription-based, self-managed version of MongoDB:MongoDB的基于订阅的自我管理版本 - MongoDB Community
: The source-available, free-to-use, and self-managed version of MongoDB:MongoDB的源代码可用、免费使用和自我管理版本
Transactions 事务API
➤ Use the Select your language drop-down menu in the upper-right to set the language of the following example.使用右上角的“选择语言”下拉菜单设置以下示例的语言。
C
This example highlights the key components of the transactions API. In particular, it uses the callback API. The callback API:本示例重点介绍了事务API的关键组件。特别是,它使用回调API。回调API:
starts a transaction启动事务executes the specified operations执行指定的操作commits the result or ends the transaction on error提交结果或在出现错误时结束事务
Errors in server-side operations, such as the 服务器端操作中的错误,如DuplicateKeyError, can end the transaction and result in a command error to alert the user that the transaction has ended. DuplicateKeyError,可能会结束事务,并导致命令错误,提醒用户事务已结束。This behavior is expected and occurs even if the client never calls 即使客户端从未调用Session.abortTransaction(). Session.abortTransaction(),也会发生这种行为。To incorporate custom error handling, use the Core API on your transaction.若要合并自定义错误处理,请在事务中使用Core API。
The callback API incorporates retry logic for certain errors. The driver tries to rerun the transaction after a TransientTransactionError or UnknownTransactionCommitResult commit error.
Starting in MongoDB 6.2, the server does not retry the transaction if it receives a TransactionTooLargeForCache error.
Starting in MongoDB 8.1, if an upsert operation runs in a multidocument transaction, then the upsert does not retry when it encounters a duplicate key error.
Important
- Use the MongoDB driver for your MongoDB version.
- When using drivers, each operation in the transaction must pass the session to each operation.
- Operations in a transaction use transaction-level read concern, transaction-level write concern, and transaction-level read preference.
- You can create collections in transactions implicitly or explicitly. See Create Collections and Indexes in a Transaction.
static bool
with_transaction_example(bson_error_t *error)
{
mongoc_client_t *client = NULL;
mongoc_write_concern_t *wc = NULL;
mongoc_collection_t *coll = NULL;
bool success = false;
bool ret = false;
bson_t *doc = NULL;
bson_t *insert_opts = NULL;
mongoc_client_session_t *session = NULL;
mongoc_transaction_opt_t *txn_opts = NULL;
/* For a replica set, include the replica set name and a seedlist of the members in the URI string;对于副本集,在URI字符串中包含副本集名称和成员的种子列表;
* e.g.
* uri_repl = "mongodb://mongodb0.example.com:27017,mongodb1.example.com:" \
* "27017/?replicaSet=myRepl";
* client = mongoc_client_new (uri_repl);
* For a sharded cluster, connect to the mongos instances;对于分片集群,连接到mongos实例; e.g.
* uri_sharded =
* "mongodb://mongos0.example.com:27017,mongos1.example.com:27017/";
* client = mongoc_client_new (uri_sharded);
*/
client = get_client();
/* Prereq: Create collections. Note Atlas connection strings include a majority write
* concern by default.
*/
wc = mongoc_write_concern_new();
mongoc_write_concern_set_wmajority(wc, 1000);
insert_opts = bson_new();
mongoc_write_concern_append(wc, insert_opts);
coll = mongoc_client_get_collection(client, "mydb1", "foo");
doc = BCON_NEW("abc", BCON_INT32(0));
ret = mongoc_collection_insert_one(coll, doc, insert_opts, NULL /* reply */, error);
if (!ret) {
goto fail;
}
bson_destroy(doc);
mongoc_collection_destroy(coll);
coll = mongoc_client_get_collection(client, "mydb2", "bar");
doc = BCON_NEW("xyz", BCON_INT32(0));
ret = mongoc_collection_insert_one(coll, doc, insert_opts, NULL /* reply */, error);
if (!ret) {
goto fail;
}
/* Step 1: Start a client session.第1步:启动客户端会话。 */
session = mongoc_client_start_session(client, NULL /* opts */, error);
if (!session) {
goto fail;
}
/* Step 2: Optional. Define options to use for the transaction.第2步:可选。定义用于事务的选项。 */
txn_opts = mongoc_transaction_opts_new();
mongoc_transaction_opts_set_write_concern(txn_opts, wc);
/* Step 3: Use mongoc_client_session_with_transaction to start a transaction,
* execute the callback, and commit (or abort on error). */
ret = mongoc_client_session_with_transaction(session, callback, txn_opts, NULL /* ctx */, NULL /* reply */, error);
if (!ret) {
goto fail;
}
success = true;
fail:
bson_destroy(doc);
mongoc_collection_destroy(coll);
bson_destroy(insert_opts);
mongoc_write_concern_destroy(wc);
mongoc_transaction_opts_destroy(txn_opts);
mongoc_client_session_destroy(session);
mongoc_client_destroy(client);
return success;
}
/* Define the callback that specifies the sequence of operations to perform
* inside the transactions. */
static bool
callback(mongoc_client_session_t *session, void *ctx, bson_t **reply, bson_error_t *error)
{
mongoc_client_t *client = NULL;
mongoc_collection_t *coll = NULL;
bson_t *doc = NULL;
bool success = false;
bool ret = false;
BSON_UNUSED(ctx);
client = mongoc_client_session_get_client(session);
coll = mongoc_client_get_collection(client, "mydb1", "foo");
doc = BCON_NEW("abc", BCON_INT32(1));
ret = mongoc_collection_insert_one(coll, doc, NULL /* opts */, *reply, error);
if (!ret) {
goto fail;
}
bson_destroy(doc);
mongoc_collection_destroy(coll);
coll = mongoc_client_get_collection(client, "mydb2", "bar");
doc = BCON_NEW("xyz", BCON_INT32(999));
ret = mongoc_collection_insert_one(coll, doc, NULL /* opts */, *reply, error);
if (!ret) {
goto fail;
}
success = true;
fail:
mongoc_collection_destroy(coll);
bson_destroy(doc);
return success;
}C++
This example highlights the key components of the transactions API. In particular, it uses the callback API. The callback API:
- starts a transaction
- executes the specified operations
- commits the result or ends the transaction on error
Errors in server-side operations, such as the DuplicateKeyError, can end the transaction and result in a command error to alert the user that the transaction has ended. This behavior is expected and occurs even if the client never calls Session.abortTransaction(). To incorporate custom error handling, use the Core API on your transaction.
The callback API incorporates retry logic for certain errors. The driver tries to rerun the transaction after a TransientTransactionError or UnknownTransactionCommitResult commit error.
Starting in MongoDB 6.2, the server does not retry the transaction if it receives a TransactionTooLargeForCache error.
Starting in MongoDB 8.1, if an upsert operation runs in a multidocument transaction, then the upsert does not retry when it encounters a duplicate key error.
Important
- Use the MongoDB driver for your MongoDB version.
- When using drivers, each operation in the transaction must pass the session to each operation.
- Operations in a transaction use transaction-level read concern, transaction-level write concern, and transaction-level read preference.
- You can create collections in transactions implicitly or explicitly. See Create Collections and Indexes in a Transaction.
// The mongocxx::instance constructor and destructor initialize and shut down the driver,
// respectively. Therefore, a mongocxx::instance must be created before using the driver and
// must remain alive for as long as the driver is in use.
mongocxx::instance inst{};
// For a replica set, include the replica set name and a seedlist of the members in the URI
// string; e.g.
// uriString =
// 'mongodb://mongodb0.example.com:27017,mongodb1.example.com:27017/?replicaSet=myRepl'
// For a sharded cluster, connect to the mongos instances; e.g.
// uriString = 'mongodb://mongos0.example.com:27017,mongos1.example.com:27017/'
mongocxx::client client{mongocxx::uri{}};
// Prepare to set majority write explicitly. Note: on Atlas deployments this won't always be
// needed. The suggested Atlas connection string includes majority write concern by default.
write_concern wc_majority{};
wc_majority.acknowledge_level(write_concern::level::k_majority);
// Prereq: Create collections.
auto foo = client["mydb1"]["foo"];
auto bar = client["mydb2"]["bar"];
try {
options::insert opts;
opts.write_concern(wc_majority);
foo.insert_one(make_document(kvp("abc", 0)), opts);
bar.insert_one(make_document(kvp("xyz", 0)), opts);
} catch (mongocxx::exception const& e) {
std::cout << "An exception occurred while inserting: " << e.what() << std::endl;
return EXIT_FAILURE;
}
// Step 1: Define the callback that specifies the sequence of operations to perform inside the
// transactions.
client_session::with_transaction_cb callback = [&](client_session* session) {
// Important:: You must pass the session to the operations.
foo.insert_one(*session, make_document(kvp("abc", 1)));
bar.insert_one(*session, make_document(kvp("xyz", 999)));
};
// Step 2: Start a client session
auto session = client.start_session();
// Step 3: Use with_transaction to start a transaction, execute the callback,
// and commit (or abort on error).
try {
options::transaction opts;
opts.write_concern(wc_majority);
session.with_transaction(callback, opts);
} catch (mongocxx::exception const& e) {
std::cout << "An exception occurred: " << e.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;C#
This example highlights the key components of the transactions API. In particular, it uses the callback API. The callback API:
- starts a transaction
- executes the specified operations
- commits the result or ends the transaction on error
Errors in server-side operations, such as the DuplicateKeyError, can end the transaction and result in a command error to alert the user that the transaction has ended. This behavior is expected and occurs even if the client never calls Session.abortTransaction(). To incorporate custom error handling, use the Core API on your transaction.
The callback API incorporates retry logic for certain errors. The driver tries to rerun the transaction after a TransientTransactionError or UnknownTransactionCommitResult commit error.
Starting in MongoDB 6.2, the server does not retry the transaction if it receives a TransactionTooLargeForCache error.
Starting in MongoDB 8.1, if an upsert operation runs in a multidocument transaction, then the upsert does not retry when it encounters a duplicate key error.
Important
- Use the MongoDB driver for your MongoDB version.
- When using drivers, each operation in the transaction must pass the session to each operation.
- Operations in a transaction use transaction-level read concern, transaction-level write concern, and transaction-level read preference.
- You can create collections in transactions implicitly or explicitly. See Create Collections and Indexes in a Transaction.
// For a replica set, include the replica set name and a seedlist of the members in the URI string; e.g.
// string uri = "mongodb://mongodb0.example.com:27017,mongodb1.example.com:27017/?replicaSet=myRepl";
// For a sharded cluster, connect to the mongos instances; e.g.
// string uri = "mongodb://mongos0.example.com:27017,mongos1.example.com:27017/";
var client = new MongoClient(connectionString);
// Prereq: Create collections.
var database1 = client.GetDatabase("mydb1");
var collection1 = database1.GetCollection<BsonDocument>("foo").WithWriteConcern(WriteConcern.WMajority);
collection1.InsertOne(new BsonDocument("abc", 0));
var database2 = client.GetDatabase("mydb2");
var collection2 = database2.GetCollection<BsonDocument>("bar").WithWriteConcern(WriteConcern.WMajority);
collection2.InsertOne(new BsonDocument("xyz", 0));
// Step 1: Start a client session.步骤1:启动客户端会话。
using (var session = client.StartSession())
{
// Step 2: Optional. Define options to use for the transaction.步骤2:可选。定义用于事务的选项。
var transactionOptions = new TransactionOptions(
writeConcern: WriteConcern.WMajority);
// Step 3: Define the sequence of operations to perform inside the transactions步骤3:定义在事务中执行的操作顺序
var cancellationToken = CancellationToken.None; // normally a real token would be used通常会使用真实的令牌
result = session.WithTransaction(
(s, ct) =>
{
try
{
collection1.InsertOne(s, new BsonDocument("abc", 1), cancellationToken: ct);
collection2.InsertOne(s, new BsonDocument("xyz", 999), cancellationToken: ct);
}
catch (MongoWriteException)
{
// Do something in response to the exception
throw; // NOTE: You must rethrow the exception otherwise an infinite loop can occur.
}
return "Inserted into collections in different databases";
},
transactionOptions,
cancellationToken);
}Go
This example highlights the key components of the transactions API. In particular, it uses the callback API. The callback API:
- starts a transaction
- executes the specified operations
- commits the result or ends the transaction on error
Errors in server-side operations, such as the DuplicateKeyError, can end the transaction and result in a command error to alert the user that the transaction has ended. This behavior is expected and occurs even if the client never calls Session.abortTransaction(). To incorporate custom error handling, use the Core API on your transaction.
The callback API incorporates retry logic for certain errors. The driver tries to rerun the transaction after a TransientTransactionError or UnknownTransactionCommitResult commit error.
Starting in MongoDB 6.2, the server does not retry the transaction if it receives a TransactionTooLargeForCache error.
Starting in MongoDB 8.1, if an upsert operation runs in a multidocument transaction, then the upsert does not retry when it encounters a duplicate key error.
Important
- Use the MongoDB driver for your MongoDB version.
- When using drivers, each operation in the transaction must pass the session to each operation.
- Operations in a transaction use transaction-level read concern, transaction-level write concern, and transaction-level read preference.
- You can create collections in transactions implicitly or explicitly. See Create Collections and Indexes in a Transaction.
// WithTransactionExample is an example of using the Session.WithTransaction function.
func WithTransactionExample(ctx context.Context) error {
// For a replica set, include the replica set name and a seedlist of the members in the URI string; e.g.
// uri := "mongodb://mongodb0.example.com:27017,mongodb1.example.com:27017/?replicaSet=myRepl"
// For a sharded cluster, connect to the mongos instances; e.g.
// uri := "mongodb://mongos0.example.com:27017,mongos1.example.com:27017/"
uri := mtest.ClusterURI()
clientOpts := options.Client().ApplyURI(uri)
client, err := mongo.Connect(clientOpts)
if err != nil {
return err
}
defer func() { _ = client.Disconnect(ctx) }()
// Prereq: Create collections.
wcMajority := writeconcern.Majority()
wcMajorityCollectionOpts := options.Collection().SetWriteConcern(wcMajority)
fooColl := client.Database("mydb1").Collection("foo", wcMajorityCollectionOpts)
barColl := client.Database("mydb1").Collection("bar", wcMajorityCollectionOpts)
// Step 1: Define the callback that specifies the sequence of operations to perform inside the transaction.
callback := func(sesctx context.Context) (any, error) {
// Important: You must pass sesctx as the Context parameter to the operations for them to be executed in the
// transaction.
if _, err := fooColl.InsertOne(sesctx, bson.D{{"abc", 1}}); err != nil {
return nil, err
}
if _, err := barColl.InsertOne(sesctx, bson.D{{"xyz", 999}}); err != nil {
return nil, err
}
return nil, nil
}
// Step 2: Start a session and run the callback using WithTransaction.
session, err := client.StartSession()
if err != nil {
return err
}
defer session.EndSession(ctx)
result, err := session.WithTransaction(ctx, callback)
if err != nil {
return err
}
log.Printf("result: %v\n", result)
return nil
}Java(Sync)
This example highlights the key components of the transactions API. In particular, it uses the callback API. The callback API:
- starts a transaction
- executes the specified operations
- commits the result or ends the transaction on error
Errors in server-side operations, such as the DuplicateKeyError, can end the transaction and result in a command error to alert the user that the transaction has ended. This behavior is expected and occurs even if the client never calls Session.abortTransaction(). To incorporate custom error handling, use the Core API on your transaction.
The callback API incorporates retry logic for certain errors. The driver tries to rerun the transaction after a TransientTransactionError or UnknownTransactionCommitResult commit error.
Starting in MongoDB 6.2, the server does not retry the transaction if it receives a TransactionTooLargeForCache error.
Starting in MongoDB 8.1, if an upsert operation runs in a multidocument transaction, then the upsert does not retry when it encounters a duplicate key error.
Important
- Use the MongoDB driver for your MongoDB version.
- When using drivers, each operation in the transaction must pass the session to each operation.
- Operations in a transaction use transaction-level read concern, transaction-level write concern, and transaction-level read preference.
- You can create collections in transactions implicitly or explicitly. See Create Collections and Indexes in a Transaction.
/*
For a replica set, include the replica set name and a seedlist of the members in the URI string; e.g.
String uri = "mongodb://mongodb0.example.com:27017,mongodb1.example.com:27017/admin?replicaSet=myRepl";
For a sharded cluster, connect to the mongos instances.
For example:
String uri = "mongodb://mongos0.example.com:27017,mongos1.example.com:27017:27017/admin";
*/
final MongoClient client = MongoClients.create(uri);
/*
Create collections.
*/
client.getDatabase("mydb1").getCollection("foo")
.withWriteConcern(WriteConcern.MAJORITY).insertOne(new Document("abc", 0));
client.getDatabase("mydb2").getCollection("bar")
.withWriteConcern(WriteConcern.MAJORITY).insertOne(new Document("xyz", 0));
/* Step 1: Start a client session.步骤1:启动客户端会话。 */
final ClientSession clientSession = client.startSession();
/* Step 2: Optional. Define options to use for the transaction.步骤2:可选。定义用于事务的选项。 */
TransactionOptions txnOptions = TransactionOptions.builder()
.writeConcern(WriteConcern.MAJORITY)
.build();
/* Step 3: Define the sequence of operations to perform inside the transactions.步骤3:定义要在事务内部执行的操作顺序。 */
TransactionBody txnBody = new TransactionBody<String>() {
public String execute() {
MongoCollection<Document> coll1 = client.getDatabase("mydb1").getCollection("foo");
MongoCollection<Document> coll2 = client.getDatabase("mydb2").getCollection("bar");
/*
Important:: You must pass the session to the operations.
*/
coll1.insertOne(clientSession, new Document("abc", 1));
coll2.insertOne(clientSession, new Document("xyz", 999));
return "Inserted into collections in different databases";
}
};
try {
/*
Step 4: Use .withTransaction() to start a transaction,
execute the callback, and commit (or abort on error).执行回调,并提交(或在出错时中止)。
*/
clientSession.withTransaction(txnBody, txnOptions);
} catch (RuntimeException e) {
// some error handling
} finally {
clientSession.close();
}Motor
This example highlights the key components of the transactions API. In particular, it uses the callback API. The callback API:本示例重点介绍了事务API的关键组件。特别是,它使用回调API。回调API:
starts a transaction启动事务executes the specified operations执行指定的操作commits the result or ends the transaction on error提交结果或在出现错误时结束事务
Errors in server-side operations, such as the DuplicateKeyError, can end the transaction and result in a command error to alert the user that the transaction has ended. This behavior is expected and occurs even if the client never calls Session.abortTransaction(). To incorporate custom error handling, use the Core API on your transaction.
The callback API incorporates retry logic for certain errors. The driver tries to rerun the transaction after a TransientTransactionError or UnknownTransactionCommitResult commit error.
Starting in MongoDB 6.2, the server does not retry the transaction if it receives a TransactionTooLargeForCache error.
Starting in MongoDB 8.1, if an upsert operation runs in a multidocument transaction, then the upsert does not retry when it encounters a duplicate key error.
Important
- Use the MongoDB driver for your MongoDB version.
- When using drivers, each operation in the transaction must pass the session to each operation.
- Operations in a transaction use transaction-level read concern, transaction-level write concern, and transaction-level read preference.
- You can create collections in transactions implicitly or explicitly. See Create Collections and Indexes in a Transaction.
# For a replica set, include the replica set name and a seedlist of the members in the URI string; e.g.
# uriString = 'mongodb://mongodb0.example.com:27017,mongodb1.example.com:27017/?replicaSet=myRepl'
# For a sharded cluster, connect to the mongos instances; e.g.
# uriString = 'mongodb://mongos0.example.com:27017,mongos1.example.com:27017/'
client = AsyncIOMotorClient(uriString)
wc_majority = WriteConcern("majority", wtimeout=1000)
# Prereq: Create collections.
await client.get_database("mydb1", write_concern=wc_majority).foo.insert_one({"abc": 0})
await client.get_database("mydb2", write_concern=wc_majority).bar.insert_one({"xyz": 0})
# Step 1: Define the callback that specifies the sequence of operations to perform inside the transactions.
async def callback(my_session):
collection_one = my_session.client.mydb1.foo
collection_two = my_session.client.mydb2.bar
# Important:: You must pass the session to the operations.
await collection_one.insert_one({"abc": 1}, session=my_session)
await collection_two.insert_one({"xyz": 999}, session=my_session)
# Step 2: Start a client session.
async with await client.start_session() as session:
# Step 3: Use with_transaction to start a transaction, execute the callback, and commit (or abort on error).
await session.with_transaction(
callback,
read_concern=ReadConcern("local"),
write_concern=wc_majority,
read_preference=ReadPreference.PRIMARY,
)Node.js
This example highlights the key components of the transactions API. In particular, it uses the callback API. The callback API:本示例重点介绍了事务API的关键组件。特别是,它使用回调API。回调API:
starts a transaction启动事务executes the specified operations执行指定的操作commits the result or ends the transaction on error提交结果或在出现错误时结束事务
Errors in server-side operations, such as the 服务器端操作中的错误,如DuplicateKeyError, can end the transaction and result in a command error to alert the user that the transaction has ended. This behavior is expected and occurs even if the client never calls Session.abortTransaction(). DuplicateKeyError,可能会结束事务,并导致命令错误,提醒用户事务已结束。即使客户端从未调用Session.abortTransaction(),也会发生这种行为。To incorporate custom error handling, use the Core API on your transaction.若要合并自定义错误处理,请在事务中使用Core API。
The callback API incorporates retry logic for certain errors. The driver tries to rerun the transaction after a TransientTransactionError or UnknownTransactionCommitResult commit error.回调API包含了针对某些错误的重试逻辑。驱动程序在TransientTransactionError或UnknownTransactionCommitResult提交错误后尝试重新运行事务。
Starting in MongoDB 6.2, the server does not retry the transaction if it receives a TransactionTooLargeForCache error.从MongoDB 6.2开始,如果服务器收到TransactionTooLargeForCache错误,则不会重试事务。
Starting in MongoDB 8.1, if an 从MongoDB 8.1开始,如果在多文档事务中运行upsert operation runs in a multidocument transaction, then the upsert does not retry when it encounters a duplicate key error.upsert操作,则当遇到重复键错误时,upsert不会重试。
Important
Use the MongoDB driver for your MongoDB version.为您的MongoDB版本使用MongoDB驱动程序。When using drivers, each operation in the transaction must pass the session to each operation.使用驱动程序时,事务中的每个操作都必须将会话传递给每个操作。Operations in a transaction use transaction-level read concern, transaction-level write concern, and transaction-level read preference.事务中的操作使用事务级读取关注、事务级写入关注和事务级读取首选项。You can create collections in transactions implicitly or explicitly. See Create Collections and Indexes in a Transaction.您可以隐式或显式地在事务中创建集合。请参见在事务中创建集合和索引。
// For a replica set, include the replica set name and a seedlist of the members in the URI string;对于副本集,在URI字符串中包含副本集名称和成员的种子列表; e.g.
// const uri = 'mongodb://mongodb0.example.com:27017,mongodb1.example.com:27017/?replicaSet=myRepl'
// For a sharded cluster, connect to the mongos instances;对于分片集群,连接到mongos实例; e.g.
// const uri = 'mongodb://mongos0.example.com:27017,mongos1.example.com:27017/'
const client = new MongoClient(uri);
await client.connect();
// Prereq: Create collections.Prereq:创建集合。
await client
.db('mydb1')
.collection('foo')
.insertOne({ abc: 0 }, { writeConcern: { w: 'majority' } });
await client
.db('mydb2')
.collection('bar')
.insertOne({ xyz: 0 }, { writeConcern: { w: 'majority' } });
// Step 1: Start a Client Session
const session = client.startSession();
// Step 2: Optional. Define options to use for the transaction步骤2:可选。定义用于事务的选项
const transactionOptions = {
readPreference: 'primary',
readConcern: { level: 'local' },
writeConcern: { w: 'majority' }
};
// Step 3: Use withTransaction to start a transaction, execute the callback, and commit (or abort on error)步骤3:使用withTransaction启动事务,执行回调,并提交(或在出错时中止)
// Note: The callback for withTransaction MUST be async and/or return a Promise.注意:withTransaction的回调必须是异步的和/或返回一个Promise。
try {
await session.withTransaction(async () => {
const coll1 = client.db('mydb1').collection('foo');
const coll2 = client.db('mydb2').collection('bar');
// Important:: You must pass the session to the operations重要提示:您必须将会话传递给操作
await coll1.insertOne({ abc: 1 }, { session });
await coll2.insertOne({ xyz: 999 }, { session });
}, transactionOptions);
} finally {
await session.endSession();
await client.close();
}Perl
This example highlights the key components of the transactions API. In particular, it uses the callback API. The callback API:
- starts a transaction
- executes the specified operations
- commits the result or ends the transaction on error
Errors in server-side operations, such as the DuplicateKeyError, can end the transaction and result in a command error to alert the user that the transaction has ended. This behavior is expected and occurs even if the client never calls Session.abortTransaction(). To incorporate custom error handling, use the Core API on your transaction.
The callback API incorporates retry logic for certain errors. The driver tries to rerun the transaction after a TransientTransactionError or UnknownTransactionCommitResult commit error.
Starting in MongoDB 6.2, the server does not retry the transaction if it receives a TransactionTooLargeForCache error.
Starting in MongoDB 8.1, if an upsert operation runs in a multidocument transaction, then the upsert does not retry when it encounters a duplicate key error.
Important
- Use the MongoDB driver for your MongoDB version.
- When using drivers, each operation in the transaction must pass the session to each operation.
- Operations in a transaction use transaction-level read concern, transaction-level write concern, and transaction-level read preference.
- You can create collections in transactions implicitly or explicitly. See Create Collections and Indexes in a Transaction.
sub runTransactionWithRetry {
my ( $txnFunc, $session ) = @_;
LOOP: {
eval {
$txnFunc->($session); # performs transaction
};
if ( my $error = $@ ) {
print("Transaction aborted-> Caught exception during transaction.\n");
# If transient error, retry the whole transaction
if ( $error->has_error_label("TransientTransactionError") ) {
print("TransientTransactionError, retrying transaction ->..\n");
redo LOOP;
}
else {
die $error;
}
}
}
return;
}
sub commitWithRetry {
my ($session) = @_;
LOOP: {
eval {
$session->commit_transaction(); # Uses write concern set at transaction start.
print("Transaction committed->\n");
};
if ( my $error = $@ ) {
# Can retry commit
if ( $error->has_error_label("UnknownTransactionCommitResult") ) {
print("UnknownTransactionCommitResult, retrying commit operation ->..\n");
redo LOOP;
}
else {
print("Error during commit ->..\n");
die $error;
}
}
}
return;
}
# Updates two collections in a transactions
sub updateEmployeeInfo {
my ($session) = @_;
my $employeesCollection = $session->client->ns("hr.employees");
my $eventsCollection = $session->client->ns("reporting.events");
$session->start_transaction(
{
readConcern => { level => "snapshot" },
writeConcern => { w => "majority" },
readPreference => 'primary',
}
);
eval {
$employeesCollection->update_one(
{ employee => 3 }, { '$set' => { status => "Inactive" } },
{ session => $session},
);
$eventsCollection->insert_one(
{ employee => 3, status => { new => "Inactive", old => "Active" } },
{ session => $session},
);
};
if ( my $error = $@ ) {
print("Caught exception during transaction, aborting->\n");
$session->abort_transaction();
die $error;
}
commitWithRetry($session);
}
# Start a session
my $session = $client->start_session();
eval {
runTransactionWithRetry(\&updateEmployeeInfo, $session);
};
if ( my $error = $@ ) {
# Do something with error
}
$session->end_session();PHP
This example highlights the key components of the transactions API. In particular, it uses the callback API. The callback API:
- starts a transaction
- executes the specified operations
- commits the result or ends the transaction on error
Errors in server-side operations, such as the DuplicateKeyError, can end the transaction and result in a command error to alert the user that the transaction has ended. This behavior is expected and occurs even if the client never calls Session.abortTransaction(). To incorporate custom error handling, use the Core API on your transaction.
The callback API incorporates retry logic for certain errors. The driver tries to rerun the transaction after a TransientTransactionError or UnknownTransactionCommitResult commit error.
Starting in MongoDB 6.2, the server does not retry the transaction if it receives a TransactionTooLargeForCache error.
Starting in MongoDB 8.1, if an upsert operation runs in a multidocument transaction, then the upsert does not retry when it encounters a duplicate key error.
Important
- Use the MongoDB driver for your MongoDB version.
- When using drivers, each operation in the transaction must pass the session to each operation.
- Operations in a transaction use transaction-level read concern, transaction-level write concern, and transaction-level read preference.
- You can create collections in transactions implicitly or explicitly. See Create Collections and Indexes in a Transaction.
/*
* For a replica set, include the replica set name and a seedlist of the members in the URI string; e.g.
* uriString = 'mongodb://mongodb0.example.com:27017,mongodb1.example.com:27017/?replicaSet=myRepl'
* For a sharded cluster, connect to the mongos instances; e.g.
* uriString = 'mongodb://mongos0.example.com:27017,mongos1.example.com:27017/'
*/
$client = new \MongoDB\Client($uriString);
// Prerequisite: Create collections.
$client->selectCollection(
'mydb1',
'foo',
[
'writeConcern' => new \MongoDB\Driver\WriteConcern(\MongoDB\Driver\WriteConcern::MAJORITY, 1000),
],
)->insertOne(['abc' => 0]);
$client->selectCollection(
'mydb2',
'bar',
[
'writeConcern' => new \MongoDB\Driver\WriteConcern(\MongoDB\Driver\WriteConcern::MAJORITY, 1000),
],
)->insertOne(['xyz' => 0]);
// Step 1: Define the callback that specifies the sequence of operations to perform inside the transactions.
$callback = function (\MongoDB\Driver\Session $session) use ($client): void {
$client
->selectCollection('mydb1', 'foo')
->insertOne(['abc' => 1], ['session' => $session]);
$client
->selectCollection('mydb2', 'bar')
->insertOne(['xyz' => 999], ['session' => $session]);
};
// Step 2: Start a client session.
$session = $client->startSession();
// Step 3: Use with_transaction to start a transaction, execute the callback, and commit (or abort on error).
\MongoDB\with_transaction($session, $callback);Python
This example highlights the key components of the transactions API. In particular, it uses the callback API. The callback API:
starts a transaction启动事务executes the specified operations执行指定的操作commits the result or ends the transaction on error提交结果或在出现错误时结束事务
Errors in server-side operations, such as the DuplicateKeyError, can end the transaction and result in a command error to alert the user that the transaction has ended. This behavior is expected and occurs even if the client never calls Session.abortTransaction(). To incorporate custom error handling, use the Core API on your transaction.
The callback API incorporates retry logic for certain errors. The driver tries to rerun the transaction after a TransientTransactionError or UnknownTransactionCommitResult commit error.
Starting in MongoDB 6.2, the server does not retry the transaction if it receives a TransactionTooLargeForCache error.
Starting in MongoDB 8.1, if an upsert operation runs in a multidocument transaction, then the upsert does not retry when it encounters a duplicate key error.
Important
- Use the MongoDB driver for your MongoDB version.
- When using drivers, each operation in the transaction must pass the session to each operation.
- Operations in a transaction use transaction-level read concern, transaction-level write concern, and transaction-level read preference.
- You can create collections in transactions implicitly or explicitly. See Create Collections and Indexes in a Transaction.
# For a replica set, include the replica set name and a seedlist of the members in the URI string; e.g.
# uriString = 'mongodb://mongodb0.example.com:27017,mongodb1.example.com:27017/?replicaSet=myRepl'
# For a sharded cluster, connect to the mongos instances; e.g.
# uriString = 'mongodb://mongos0.example.com:27017,mongos1.example.com:27017/'
client = MongoClient(uriString)
wc_majority = WriteConcern("majority", wtimeout=1000)
# Prereq: Create collections.
client.get_database("mydb1", write_concern=wc_majority).foo.insert_one({"abc": 0})
client.get_database("mydb2", write_concern=wc_majority).bar.insert_one({"xyz": 0})
# Step 1: Define the callback that specifies the sequence of operations to perform inside the transactions.
def callback(session):
collection_one = session.client.mydb1.foo
collection_two = session.client.mydb2.bar
# Important:: You must pass the session to the operations.
collection_one.insert_one({"abc": 1}, session=session)
collection_two.insert_one({"xyz": 999}, session=session)
# Step 2: Start a client session.
with client.start_session() as session:
# Step 3: Use with_transaction to start a transaction, execute the callback, and commit (or abort on error).
session.with_transaction(callback)Ruby
This example highlights the key components of the transactions API. In particular, it uses the callback API. The callback API:
- starts a transaction
- executes the specified operations
- commits the result or ends the transaction on error
Errors in server-side operations, such as the DuplicateKeyError, can end the transaction and result in a command error to alert the user that the transaction has ended. This behavior is expected and occurs even if the client never calls Session.abortTransaction(). To incorporate custom error handling, use the Core API on your transaction.
The callback API incorporates retry logic for certain errors. The driver tries to rerun the transaction after a TransientTransactionError or UnknownTransactionCommitResult commit error.
Starting in MongoDB 6.2, the server does not retry the transaction if it receives a TransactionTooLargeForCache error.
Starting in MongoDB 8.1, if an upsert operation runs in a multidocument transaction, then the upsert does not retry when it encounters a duplicate key error.
Important
- Use the MongoDB driver for your MongoDB version.
- When using drivers, each operation in the transaction must pass the session to each operation.
- Operations in a transaction use transaction-level read concern, transaction-level write concern, and transaction-level read preference.
- You can create collections in transactions implicitly or explicitly. See Create Collections and Indexes in a Transaction.
# For a replica set, include the replica set name and a seedlist of the members in the URI string; e.g.
# uriString = 'mongodb://mongodb0.example.com:27017,mongodb1.example.com:27017/?replicaSet=myRepl'
# For a sharded cluster, connect to the mongos instances; e.g.
# uri_string = 'mongodb://mongos0.example.com:27017,mongos1.example.com:27017/'
client = Mongo::Client.new(uri_string, write_concern: {w: :majority, wtimeout: 1000})
# Prereq: Create collections.
client.use('mydb1')['foo'].insert_one(abc: 0)
client.use('mydb2')['bar'].insert_one(xyz: 0)
# Step 1: Define the callback that specifies the sequence of operations to perform inside the transactions.
callback = Proc.new do |my_session|
collection_one = client.use('mydb1')['foo']
collection_two = client.use('mydb2')['bar']
# Important: You must pass the session to the operations.
collection_one.insert_one({'abc': 1}, session: my_session)
collection_two.insert_one({'xyz': 999}, session: my_session)
end
#. Step 2: Start a client session.
session = client.start_session
# Step 3: Use with_transaction to start a transaction, execute the callback, and commit (or abort on error).
session.with_transaction(
read_concern: {level: :local},
write_concern: {w: :majority, wtimeout: 1000},
read: {mode: :primary},
&callback)Rust
This example highlights the key components of the transactions API. In particular, it uses the callback API. The callback API:
- starts a transaction
- executes the specified operations
- commits the result or ends the transaction on error
Errors in server-side operations, such as the DuplicateKeyError, can end the transaction and result in a command error to alert the user that the transaction has ended. This behavior is expected and occurs even if the client never calls Session.abortTransaction(). To incorporate custom error handling, use the Core API on your transaction.
The callback API incorporates retry logic for certain errors. The driver tries to rerun the transaction after a TransientTransactionError or UnknownTransactionCommitResult commit error.
Starting in MongoDB 6.2, the server does not retry the transaction if it receives a TransactionTooLargeForCache error.
Starting in MongoDB 8.1, if an upsert operation runs in a multidocument transaction, then the upsert does not retry when it encounters a duplicate key error.
Important
- Use the MongoDB driver for your MongoDB version.
- When using drivers, each operation in the transaction must pass the session to each operation.
- Operations in a transaction use transaction-level read concern, transaction-level write concern, and transaction-level read preference.
- You can create collections in transactions implicitly or explicitly. See Create Collections and Indexes in a Transaction.
// Prereq: Create collections. CRUD operations in transactions must be on existing collections.
client
.database("mydb1")
.collection::<Document>("foo")
.insert_one(doc! { "abc": 0})
.await?;
client
.database("mydb2")
.collection::<Document>("bar")
.insert_one(doc! { "xyz": 0})
.await?;
// Step 1: Define the callback that specifies the sequence of operations to perform inside the
// transaction.
async fn callback(session: &mut ClientSession) -> Result<()> {
let collection_one = session
.client()
.database("mydb1")
.collection::<Document>("foo");
let collection_two = session
.client()
.database("mydb2")
.collection::<Document>("bar");
// Important: You must pass the session to the operations.
collection_one
.insert_one(doc! { "abc": 1 })
.session(&mut *session)
.await?;
collection_two
.insert_one(doc! { "xyz": 999 })
.session(session)
.await?;
Ok(())
}
// Step 2: Start a client session.
let mut session = client.start_session().await?;
// Step 3: Use and_run2 to start a transaction, execute the callback, and commit (or
// abort on error).
session.start_transaction().and_run2(callback).await?;Scala
This example uses the core API. Because the core API does not incorporate retry logic for the TransientTransactionError or UnknownTransactionCommitResult commit errors, the example includes explicit logic to retry the transaction for these errors:
Important
- Use the MongoDB driver for your MongoDB version.
- When using drivers, each operation in the transaction must pass the session to each operation.
- Operations in a transaction use transaction-level read concern, transaction-level write concern, and transaction-level read preference.
- You can create collections in transactions implicitly or explicitly. See Create Collections and Indexes in a Transaction.
/*
* Copyright 2008-present MongoDB, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mongodb.scala
import org.mongodb.scala.model.{Filters, Updates}
import org.mongodb.scala.result.UpdateResult
import scala.concurrent.Await
import scala.concurrent.duration.Duration
//scalastyle:off magic.number
class DocumentationTransactionsExampleSpec extends RequiresMongoDBISpec {
// Implicit functions that execute the Observable and return the results
val waitDuration = Duration(5, "seconds")
implicit class ObservableExecutor[T](observable: Observable[T]) {
def execute(): Seq[T] = Await.result(observable.toFuture(), waitDuration)
}
implicit class SingleObservableExecutor[T](observable: SingleObservable[T]) {
def execute(): T = Await.result(observable.toFuture(), waitDuration)
}
// end implicit functions
"The Scala driver" should "be able to commit a transaction" in withClient { client =>
assume(serverVersionAtLeast(List(4, 0, 0)) && !hasSingleHost())
client.getDatabase("hr").drop().execute()
client.getDatabase("hr").createCollection("employees").execute()
client.getDatabase("hr").createCollection("events").execute()
updateEmployeeInfoWithRetry(client).execute() should equal(Completed())
client.getDatabase("hr").drop().execute() should equal(Completed())
}
def updateEmployeeInfo(database: MongoDatabase, observable: SingleObservable[ClientSession]): SingleObservable[ClientSession] = {
observable.map(clientSession => {
val employeesCollection = database.getCollection("employees")
val eventsCollection = database.getCollection("events")
val transactionOptions = TransactionOptions.builder()
.readPreference(ReadPreference.primary())
.readConcern(ReadConcern.SNAPSHOT)
.writeConcern(WriteConcern.MAJORITY)
.build()
clientSession.startTransaction(transactionOptions)
employeesCollection.updateOne(clientSession, Filters.eq("employee", 3), Updates.set("status", "Inactive"))
.subscribe((res: UpdateResult) => println(res))
eventsCollection.insertOne(clientSession, Document("employee" -> 3, "status" -> Document("new" -> "Inactive", "old" -> "Active")))
.subscribe((res: Completed) => println(res))
clientSession
})
}
def commitAndRetry(observable: SingleObservable[Completed]): SingleObservable[Completed] = {
observable.recoverWith({
case e: MongoException if e.hasErrorLabel(MongoException.UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL) => {
println("UnknownTransactionCommitResult, retrying commit operation ...")
commitAndRetry(observable)
}
case e: Exception => {
println(s"Exception during commit ...: $e")
throw e
}
})
}
def runTransactionAndRetry(observable: SingleObservable[Completed]): SingleObservable[Completed] = {
observable.recoverWith({
case e: MongoException if e.hasErrorLabel(MongoException.TRANSIENT_TRANSACTION_ERROR_LABEL) => {
println("TransientTransactionError, aborting transaction and retrying ...")
runTransactionAndRetry(observable)
}
})
}
def updateEmployeeInfoWithRetry(client: MongoClient): SingleObservable[Completed] = {
val database = client.getDatabase("hr")
val updateEmployeeInfoObservable: Observable[ClientSession] = updateEmployeeInfo(database, client.startSession())
val commitTransactionObservable: SingleObservable[Completed] =
updateEmployeeInfoObservable.flatMap(clientSession => clientSession.commitTransaction())
val commitAndRetryObservable: SingleObservable[Completed] = commitAndRetry(commitTransactionObservable)
runTransactionAndRetry(commitAndRetryObservable)
}
}Tip
For an example in 有关mongosh, see mongosh Example.mongosh的示例,请参阅mongosh示例。
Transactions and Atomicity事务和原子性
For situations that require atomicity of reads and writes to multiple documents (in a single or multiple collections), MongoDB supports distributed transactions, including transactions on replica sets and sharded clusters.对于需要对多个文档(在单个或多个集合中)进行读写原子性的情况,MongoDB支持分布式事务,包括副本集和分片集群上的事务。
Distributed transactions are atomic:分布式事务是原子性的:
Transactions either apply all data changes or roll back the changes.事务要么应用所有数据更改,要么回滚更改。If a transaction commits, all data changes made in the transaction are saved and are visible outside of the transaction.如果事务提交,则事务中所做的所有数据更改都将被保存,并在事务外部可见。Until a transaction commits, the data changes made in the transaction are not visible outside the transaction.在事务提交之前,事务中所做的数据更改在事务外部不可见。However, when a transaction writes to multiple shards, not all outside read operations need to wait for the result of the committed transaction to be visible across the shards.然而,当一个事务写入多个分片时,并非所有外部读取操作都需要等待提交事务的结果在分片之间可见。For example, if a transaction is committed and write 1 is visible on shard A but write 2 is not yet visible on shard B, an outside read at read concern例如,如果一个事务已提交,并且写1在分片a上可见,但写2在分片B上尚不可见,则外部读取关注"local"can read the results of write 1 without seeing write 2."local"可以读取写1的结果,而看不到写2。When a transaction aborts, all data changes made in the transaction are discarded without ever becoming visible. For example, if any operation in the transaction fails, the transaction aborts and all data changes made in the transaction are discarded without ever becoming visible.当事务中止时,事务中所做的所有数据更改都会被丢弃,而不会变得可见。例如,如果事务中的任何操作失败,事务将中止,事务中所做的所有数据更改都将被丢弃,而不会变得可见。
Important
In most cases, a distributed transaction incurs a greater performance cost over single document writes, and the availability of distributed transactions should not be a replacement for effective schema design. 在大多数情况下,分布式事务比单文档写入产生更大的性能成本,分布式事务的可用性不应取代有效的模式设计。For many scenarios, the denormalized data model (embedded documents and arrays) will continue to be optimal for your data and use cases. That is, for many scenarios, modeling your data appropriately will minimize the need for distributed transactions.对于许多场景,非规范化数据模型(嵌入式文档和数组)将继续是您的数据和用例的最佳选择。也就是说,对于许多场景,适当地对数据进行建模将最大限度地减少对分布式事务的需求。
For additional transactions usage considerations (such as runtime limit and oplog size limit), see also Production Considerations.有关其他事务使用注意事项(如运行时限制和oplog大小限制),另请参阅生产注意事项。
Transactions and Operations事务和运营
Distributed transactions can be used across multiple operations, collections, databases, documents, and shards.分布式事务可以跨多个操作、集合、数据库、文档和分片使用。
For transactions:对于事务:
You can create collections and indexes in transactions. For details, see Create Collections and Indexes in a Transaction您可以在事务中创建集合和索引。有关详细信息,请参阅在事务中创建集合和索引The collections used in a transaction can be in different databases.事务中使用的集合可以在不同的数据库中。Note
You cannot create new collections in cross-shard write transactions. For example, if you write to an existing collection in one shard and implicitly create a collection in a different shard, MongoDB cannot perform both operations in the same transaction.您无法在跨分片写入事务中创建新集合。例如,如果你在一个分片中写入一个现有的集合,并在另一个分片中隐式创建一个集合,MongoDB无法在同一事务中执行这两个操作。You cannot write to capped collections.您不能写入封顶集合。You cannot use read concern从封顶集合中读取时,不能使用读取关注"snapshot"when reading from a capped collection. (Starting in MongoDB 5.0)"snapshot"。(从MongoDB 5.0开始)You cannot read/write to collections in the您无法对config,admin, orlocaldatabases.config、admin或local数据库中的集合进行读写操作。You cannot write to您无法写入system.*collections.system.*集合。You cannot return the supported operation's query plan using您无法使用explainor similar commands.explain或类似命令返回受支持操作的查询计划。
For cursors created outside of a transaction, you cannot call对于在事务外部创建的游标,您不能在事务内部调用getMoreinside the transaction.getMore。For cursors created in a transaction, you cannot call对于在事务中创建的游标,您不能在事务外部调用getMoreoutside the transaction.getMore。
You cannot specify the不能将killCursorscommand as the first operation in a transaction.killCursors命令指定为事务中的第一个操作。Additionally, if you run the此外,如果在事务中运行killCursorscommand within a transaction, the server immediately stops the specified cursors. It does not wait for the transaction to commit.killCursors命令,服务器会立即停止指定的游标。它不会等待事务提交。
For a list of operations not supported in transactions, see Restricted Operations.有关事务中不支持的操作列表,请参阅受限操作。
Tip
When creating or dropping a collection immediately before starting a transaction, if the collection is accessed within the transaction, issue the create or drop operation with write concern 在启动事务之前立即创建或删除集合时,如果在事务中访问了该集合,则发出带有写关注"majority" to ensure that the transaction can acquire the required locks."majority"的创建或删除操作,以确保事务能够获取所需的锁。
Create Collections and Indexes in a Transaction在事务中创建集合和索引
You can perform the following operations in a distributed transaction if the transaction is not a cross-shard write transaction:如果分布式事务不是跨分片写入事务,则可以在该事务中执行以下操作:
Create collections.创建集合。Create indexes on new empty collections created earlier in the same transaction.在同一事务中较早创建的新空集合上创建索引。
When creating a collection inside a transaction:在事务中创建集合时:
You can implicitly create a collection, such as with:您可以隐式创建一个集合,例如:an insert operation for a non-existent collection, or对不存在的集合执行插入操作,或an update/findAndModify operation with对于不存在的集合,执行带有upsert: truefor a non-existent collection.upsert:true的update/findAndModify操作。
You can explicitly create a collection using the您可以使用createcommand or its helperdb.createCollection().create命令或其助手db.createCollection()显式创建集合。
When creating an index inside a transaction [1], the index to create must be on either:在事务中创建索引时[1],要创建的索引必须位于以下任一位置:
a non-existent collection. The collection is created as part of the operation.一个不存在的集合。该集合是作为操作的一部分创建的。a new empty collection created earlier in the same transaction.在同一事务中较早创建的新空集合。
| [1] | db.collection.createIndex() and db.collection.createIndexes() on existing indexes to check for existence. These operations return successfully without creating the index.db.collection.createIndex()和db.collection.createIndexes()来检查是否存在。这些操作在不创建索引的情况下成功返回。 |
Restrictions限制
You cannot create new collections in cross-shard write transactions. For example, if you write to an existing collection in one shard and implicitly create a collection in a different shard, MongoDB cannot perform both operations in the same transaction.您无法在跨分片写入事务中创建新集合。例如,如果你在一个分片中写入一个现有的集合,并在另一个分片中隐式创建一个集合,MongoDB无法在同一事务中执行这两个操作。You cannot use the在针对分片集合时,您不能在事务中使用$graphLookupstage within a transaction while targeting a sharded collection.$graphLookup阶段。For explicit creation of a collection or an index inside a transaction, the transaction read concern level must be为了在事务中显式创建集合或索引,事务读取关注级别必须是"local"."local"的。To explicitly create collections and indexes, use the following commands and methods:要显式创建集合和索引,请使用以下命令和方法:Command命令Method方法createdb.createCollection()createIndexesdb.collection.createIndex()db.collection.createIndexes()
Count Operation计数操作
To perform a count operation within a transaction, use the 要在事务中执行计数操作,请使用$count aggregation stage or the $group (with a $sum expression) aggregation stage.$count聚合阶段或$group(带$sum表达式)聚合阶段。
MongoDB drivers provide a collection-level API MongoDB驱动程序提供了一个集合级API countDocuments(filter, options) as a helper method that uses the $group with a $sum expression to perform a count.countDocuments(filter, options)作为一个助手方法,使用带有$sum表达式的$group来执行计数。
mongosh provides the 提供db.collection.countDocuments() helper method that uses the $group with a $sum expression to perform a count.db.collection.countDocuments()辅助方法,该方法使用带有$sum表达式的$group来执行计数。
Distinct Operation去除重复操作
To perform a distinct operation within a transaction:要在事务中执行去除重复的操作,请执行以下操作:
For unsharded collections, you can use the对于未记录的集合,您可以使用db.collection.distinct()method/thedistinctcommand as well as the aggregation pipeline with the$groupstage.db.collection.distinct()方法/distinct命令以及$group阶段的聚合管道。For sharded collections, you cannot use the对于分片集合,不能使用db.collection.distinct()method or thedistinctcommand.db.collection.distinct()方法或distinct命令。To find the distinct values for a sharded collection, use the aggregation pipeline with the要查找分片集合的不同值,请使用带有$groupstage instead. For example:$group阶段的聚合管道。例如:Instead of
db.coll.distinct("x"), usedb.coll.aggregate([
{ $group: { _id: null, distinctValues: { $addToSet: "$x" } } },
{ $project: { _id: 0 } }
])Instead of请用以下方式代替db.coll.distinct("x", { status: "A" }), use:db.coll.distinct("x", { status: "A" }):db.coll.aggregate([
{ $match: { status: "A" } },
{ $group: { _id: null, distinctValues: { $addToSet: "$x" } } },
{ $project: { _id: 0 } }
])
The pipeline returns a cursor to a document:管道将游标返回到文档:{ "distinctValues" : [ 2, 3, 1 ] }Iterate the cursor to access the results document.迭代游标以访问结果文档。
Informational Operations信息化运营
Informational commands, such as 事务中允许使用信息命令,如hello, buildInfo, connectionStatus (and their helper methods) are allowed in transactions; however, they cannot be the first operation in the transaction.hello、buildInfo、connectionStatus(及其辅助方法);但是,它们不能是事务中的第一个操作。
Restricted Operations受限操作
The following operations are not allowed in transactions:事务中不允许执行以下操作:
Creating new collections in cross-shard write transactions. For example, if you write to an existing collection in one shard and implicitly create a collection in a different shard, MongoDB cannot perform both operations in the same transaction.在跨分片写入事务中创建新集合。例如,如果你在一个分片中写入一个现有的集合,并在另一个分片中隐式创建一个集合,MongoDB无法在同一事务中执行这两个操作。Explicit creation of collections, e.g.当使用“本地”以外的读取关注级别时,显式创建集合,例如db.createCollection()method, and indexes, e.g.db.collection.createIndexes()anddb.collection.createIndex()methods, when using a read concern level other than"local".db.createCollection()方法,以及索引,例如db.collection.createIndexes()和db.collection.createIndex()方法。ThelistCollectionsandlistIndexescommands and their helper methods.listCollections和listIndexes命令及其辅助方法。Other non-CRUD and non-informational operations, such as其他非CRUD和非信息性操作,如createUser,getParameter,count, etc. and their helpers.createUser、getParameter、count等及其助手。Parallel operations. To update multiple namespaces concurrently, consider using the并行操作。要同时更新多个命名空间,请考虑使用bulkWritecommand instead.bulkWrite命令。
Transactions and Sessions事务和会话
Transactions are associated with a session.事务与会话相关联。You can have at most one open transaction at a time for a session.在一个会话中,一次最多只能有一个未结事务。When using the drivers, each operation in the transaction must be associated with the session. Refer to your driver specific documentation for details.使用驱动程序时,事务中的每个操作都必须与会话相关联。有关详细信息,请参阅特定于驾驶员的文档。If a session ends and it has an open transaction, the transaction aborts.如果会话结束并且有一个打开的事务,则事务中止。
Read Concern/Write Concern/Read Preference读取关注/写入关注/读取偏好
Transactions and Read Preference事务和读取偏好
Operations in a transaction use the transaction-level read preference.事务中的操作使用事务级读取首选项。
Using the drivers, you can set the transaction-level read preference at the transaction start:使用驱动程序,您可以在事务开始时设置事务级别的读取首选项:
If the transaction-level read preference is unset, the transaction uses the session-level read preference.如果未设置事务级读取首选项,则事务将使用会话级读取首参数。If transaction-level and the session-level read preference are unset, the transaction uses the client-level read preference. By default, the client-level read preference is如果未设置事务级别和会话级别的读取首选项,则事务将使用客户端级别的读取首选项。默认情况下,客户端级别的读取首选项是primary.primary。
Distributed transactions that contain read operations must use read preference 包含读取操作的分布式事务必须使用读取首选项primary. All operations in a given transaction must route to the same member.primary。给定事务中的所有操作都必须路由到同一成员。
Transactions and Read Concern事务和读取关注
Operations in a transaction use the transaction-level read concern. This means a read concern set at the collection and database level is ignored inside the transaction.事务中的操作使用事务级读取关注。这意味着在事务中忽略了在集合和数据库级别设置的读取关注。
You can set the transaction-level read concern at the transaction start.您可以在事务开始时设置事务级别的读取关注。
If the transaction-level read concern is unset, the transaction-level read concern defaults to the session-level read concern.如果事务级读取关注未设置,则事务级读取关系默认为会话级读取关系。If transaction-level and the session-level read concern are unset, the transaction-level read concern defaults to the client-level read concern.如果未设置事务级和会话级读取关注,则事务级读取关注将默认为客户端级读取关注。By default, the client-level read concern is默认情况下,对于主服务器上的读取,客户端级别的读取关注是"local"for reads on the primary. See also:"local"的。另请参见:
Transactions support the following read concern levels:事务支持以下读取关注级别:
"local"
Read concern读取关注"local"returns the most recent data available from the node but can be rolled back."local"返回节点上可用的最新数据,但可以回滚。On a replica set, even if a transaction uses read concern在副本集上,即使事务使用local, you might observe stronger read isolation where the operation reads from a snapshot at the point in time that the transaction was opened.local读取关注,您也可能会观察到更强的读取隔离,即操作在事务打开的时间点从快照中读取。For transactions on sharded cluster,对于分片集群上的事务,"local"read concern cannot guarantee that the data is from the same snapshot view across the shards."local"读取关注无法保证数据来自分片上的同一快照视图。If snapshot isolation is required, use如果需要快照隔离,请使用"snapshot"read concern."snapshot"读取关注。You can create collections and indexes inside a transaction.您可以在事务中创建集合和索引。If explicitly creating a collection or an index, the transaction must use read concern如果显式创建集合或索引,事务必须使用读取关注"local"."local"。If you implicitly create a collection, you can use any of the read concerns available for transactions.如果隐式创建集合,则可以使用事务可用的任何读取关注。
"majority"
If the transaction commits with write concern "majority", read concern如果事务以写关注“多数”提交,则读关注"majority"returns data that has been acknowledged by a majority of the replica set members and can't be rolled back."majority"返回已被大多数副本集成员确认且无法回滚的数据。Otherwise, read concern否则,读取关注"majority"provides no guarantees that read operations read majority-committed data."majority"不能保证读取操作读取大多数提交的数据。For transactions on sharded cluster, read concern对于分片集群上的事务,读取关注"majority"can't guarantee that the data is from the same snapshot view across the shards."majority"无法保证数据来自分片上的同一快照视图。If snapshot isolation is required, use read concern如果需要快照隔离,请使用读取关注"snapshot"."snapshot"。
"snapshot"
Read concern如果事务以写入关注"snapshot"returns data from a snapshot of majority committed data if the transaction commits with write concern "majority"."majority"提交,则读关注"snapshot"将从多数提交数据的快照中返回数据。If the transaction does not use write concern "majority" for the commit, the如果事务不使用写入关注"snapshot"read concern provides no guarantee that read operations used a snapshot of majority-committed data."majority"进行提交,则"snapshot"读关注不能保证读操作使用了多数提交数据的快照。For transactions on sharded clusters, the对于分片集群上的事务,数据的"snapshot"view of the data is synchronized across shards."snapshot"视图在分片之间同步。
Transactions and Write Concern事务和写入关注
Transactions use the transaction-level write concern to commit the write operations. 事务使用事务级写入关注来提交写入操作。Write operations inside transactions must be run without an explicit write concern specification and use the default write concern. At commit time, the writes committed using the transaction-level write concern.事务内的写入操作必须在没有明确写入关注规范的情况下运行,并使用默认写入关注。在提交时,使用事务级写入关注提交的写入。
Tip
Don't explicitly set the write concern for the individual write operations inside a transaction. Setting write concerns for the individual write operations inside a transaction returns an error.不要为事务中的各个写操作明确设置写关注。为事务内的各个写入操作设置写入关注会返回错误。
You can set the transaction-level write concern at the transaction start:您可以在事务开始时设置事务级写入关注:
If the transaction-level write concern is unset, the transaction-level write concern defaults to the session-level write concern for the commit.如果未设置事务级写入关注,则事务级写入关心点默认为提交的会话级写入关注。If the transaction-level write concern and the session-level write concern are unset, the transaction-level write concern defaults to the client-level write concern of:如果未设置事务级写入关注和会话级写入关注,则事务级写入关系默认为客户端级写入关注:MongoDB 5.0及更高版本中的w: "majority"in MongoDB 5.0 and later, with differences for deployments containing arbiters.w: "majority",但包含仲裁器的部署有所不同。See Implicit Default Write Concern.请参阅隐式默认写入关注。w: 1
Transactions support all write concern w values, including:事务支持所有写关注w值,包括:
w: 1
Write concern写关注w: 1returns acknowledgment after the commit has been applied to the primary.w: 1在提交应用于主节点后返回确认。Important
When you commit with当您使用w: 1, your transaction can be rolled back if there is a failover.w: 1提交时,如果发生故障转移,事务可以回滚。When you commit with当您使用w: 1write concern, transaction-level"majority"read concern provides no guarantees that read operations in the transaction read majority-committed data.w: 1写入关注提交时,事务级"majority"读取关注无法保证事务中的读取操作读取多数提交的数据。When you commit with当您使用w: 1write concern, transaction-level"snapshot"read concern provides no guarantee that read operations in the transaction used a snapshot of majority-committed data.w: 1写入关注提交时,事务级"snapshot"读取关注并不能保证事务中的读取操作使用了大多数提交数据的快照。
w: "majority"
Write concern写关注w: "majority"returns acknowledgment after the commit has been applied to a majority of voting members.w: "majority"在承诺被应用于大多数有表决权的成员后返回确认。When you commit with当您使用w: "majority"write concern, transaction-level"majority"read concern guarantees that operations have read majority-committed data. For transactions on sharded clusters, this view of the majority-committed data is not synchronized across shards.w: "majority"写关注提交时,事务级"majority"读关注保证操作已读取多数提交的数据。对于分片集群上的事务,大多数提交数据的视图不会在分片之间同步。When you commit with当您使用w: "majority"write concern, transaction-level"snapshot"read concern guarantees that operations have read from a synchronized snapshot of majority-committed data.w: "majority"写关注提交时,事务级"snapshot"读关注保证操作已从多数提交数据的同步快照中读取。
Note
Regardless of the write concern specified for the transaction, the commit operation for a sharded cluster transaction includes some parts that use 无论为事务指定了什么写关注,分片集群事务的提交操作都包括一些使用{w: "majority", j: true} write concern.{w: "majority", j: true}写关注的部分。
The server parameter 服务器参数coordinateCommitReturnImmediatelyAfterPersistingDecision controls when transaction commit decisions are returned to the client.coordinateCommitReturnImmediatelyAfterPersistingDecision控制何时将事务提交决策返回给客户端。
The parameter was introduced in MongDB 5.0 with a default value of 该参数在MongDB 5.0中引入,默认值为true. In MongoDB 6.1 the default value changes to false.true。在MongoDB 6.1中,默认值更改为false。
When 当coordinateCommitReturnImmediatelyAfterPersistingDecision is false, the shard transaction coordinator waits for all members to acknowledge a multi-document transaction commit before returning the commit decision to the client.coordinateCommitReturnImmediatelyAfterPersistingDecision为false时,分片事务协调器会等待所有成员确认多文档事务提交,然后再将提交决定返回给客户端。
If you specify a 如果为单文档和多文档写入指定"majority" write concern for single-document and multi-document writes and the operation does not replicate to the calculated majority of replica set members before it returns a response, then the data eventually replicates or rolls back. "majority"写入关注,并且操作在返回响应之前没有复制到计算出的多数副本集成员,则数据最终会复制或回滚。See 请参阅wtimeout.wtimeout。
Regardless of the write concern specified for the transaction, the driver applies 无论为事务指定了什么写入关注,在重试w: "majority" as the write concern when retrying commitTransaction.commitTransaction时,驱动程序都会将w: "majority"应用为写入关注。
General Information一般信息
The following sections describe more considerations for transactions.以下部分描述了事务的更多注意事项。
Production Considerations生产注意事项
For transactions in production environments, see Production Considerations. 有关生产环境中的事务,请参阅生产注意事项。In addition, for sharded clusters, see Production Considerations (Sharded Clusters).此外,关于分片集群,请参阅生产注意事项(分片集群)。
Arbiters仲裁者
You cannot change a shard key using a transaction if the replica set has an arbiter. Arbiters cannot participate in the data operations required for multi-shard transactions.如果副本集有仲裁器,则无法使用事务更改分片键。仲裁员不能参与多分片事务所需的数据操作。
Transactions whose write operations span multiple shards will error and abort if any transaction operation reads from or writes to a shard that contains an arbiter.如果任何事务操作从包含仲裁器的分片读取或写入,则写操作跨越多个分片的事务将出错并中止。
Shard Configuration Restriction分片配置限制
You cannot run transactions on a sharded cluster that has a shard with 您无法在具有writeConcernMajorityJournalDefault set to false (such as a shard with a voting member that uses the in-memory storage engine).writeConcernMajorityJournalDefault设置为false的分片的分片集群上运行事务(例如具有使用内存存储引擎的投票成员的分片)。
Note
Regardless of the write concern specified for the transaction, the commit operation for a sharded cluster transaction includes some parts that use 无论为事务指定了什么写关注,分片集群事务的提交操作都包括一些使用{w: "majority", j: true} write concern.{w: "majority", j: true}写关注的部分。
Diagnostics诊断
To obtain transaction status and metrics, use the following methods:要获取事务状态和指标,请使用以下方法:
| Source | Returns |
|---|---|
db.serverStatus() methodserverStatus command | Returns transactions metrics.
|
$currentOp |
|
db.currentOp() methodcurrentOp command |
|
mongod and mongos | operationProfiling.slowOpThresholdMs threshold) in the TXN log component.TXN日志组件中包括有关慢速事务(即超过operationProfiling.slowOpThresholdMs阈值的事务)的信息。 |
Feature Compatibility Version (FCV)功能兼容性版本(FCV)
To use transactions, the featureCompatibilityVersion for all members of the deployment must be at least:要使用事务,部署所有成员的featureCompatibilityVersion必须至少为:
featureCompatibilityVersion | |
|---|---|
4.0 | |
4.2 |
To check the FCV for a member, connect to the member and run the following command:要检查成员的FCV,请连接到该成员并运行以下命令:
db.adminCommand( { getParameter: 1, featureCompatibilityVersion: 1 } )
For more information, see the setFeatureCompatibilityVersion reference page.
Storage Engines存储引擎
distributed transactions are supported on replica sets and sharded clusters where:副本集和分片集群支持分布式事务,其中:
the primary uses the WiredTiger storage engine, and主要使用WiredTiger存储引擎,以及the secondary members use either the WiredTiger storage engine or the in-memory storage engines.次要成员使用WiredTiger存储引擎或内存中存储引擎。
Note
You cannot run transactions on a sharded cluster that has a shard with 您无法在具有writeConcernMajorityJournalDefault set to false, such as a shard with a voting member that uses the in-memory storage engine.writeConcernMajorityJournalDefault设置为false的分片的分片集群上运行事务,例如具有使用内存存储引擎的投票成员的分片。
Limit Critical Section Wait Time限制关键部分等待时间
Starting in MongoDB 5.2 (and 5.0.4):从MongoDB 5.2(和5.0.4)开始:
When a query accesses a shard, a chunk migration or DDL operation may hold the critical section for the collection.当查询访问分片时,块迁移或DDL操作可能会保留集合的关键部分。To limit the time a shard waits for a critical section within a transaction, use the要限制分片等待事务中关键部分的时间,请使用metadataRefreshInTransactionMaxWaitMSparameter.metadataRefreshInTransactionMaxWaitMS参数。Note
Starting in MongoDB 8.1, the old从MongoDB 8.1开始,旧的metadataRefreshInTransactionMaxWaitBehindCritSecMSparameter is renamedmetadataRefreshInTransactionMaxWaitMS. You can continue to usemetadataRefreshInTransactionMaxWaitBehindCritSecMSas the parameter name, but it is deprecated and will be removed in a future MongoDB release.metadataRefreshInTransactionMaxWaitBehindCritSecMS参数被重命名为metadataRefreshInTransactionMaxWaitMS。您可以继续使用metadataRefreshInTransactionMaxWaitBehindCritSecMS作为参数名称,但它已被弃用,并将在未来的MongoDB版本中删除。