Overview
在本指南中,您可以学习;了解如何使用MongoDB C++驱动程序来执行事务。事务允许您运行一系列操作,这些操作在提交ACID 事务之前不会更改任何数据。如果ACID 事务中的任何操作返回错误,驱动程序就会取消ACID 事务,并在所有数据更改变得可见之前将其丢弃。
在MongoDB中,事务在逻辑会话中运行。会话是您打算按顺序运行的一组相关读取或写入操作。 会话为符合ACID的ACID 事务中的一群组操作启用了因果一致性,该ACID 事务满足原子性、一致性、隔离性和持久性的预期。MongoDBACID 一致性保证ACID 事务操作中涉及的数据保持一致,即使操作遇到意外错误。
使用C++驱动程序时,您可以从 mongocxx::client实例创建新会话。然后,您可以使用生成的 mongocxx::client_session实例来执行事务。我们建议您将客户端重复用于多个会话和事务,而不是每次都实例化一个新客户端。
警告
仅将 mongocxx::client_session 与创建它的 mongocxx::client 一起使用。 将 client_session 与不同的 client 一起使用会导致操作错误。
重要
mongocxx::client 的实例不是线程安全的。 每个 mongoxcc::client实例及其子实例(包括 mongocxx::client_session)一次应由一个线程使用。 要学习;了解更多信息,请参阅 Thread and Fork 安全指南。
事务 API
MongoDB C++驱动程序提供了 Callback API和 Core API来管理ACID 事务方式。 在开始ACID 事务之前,必须调用 start_session() 方法来实例化 mongocxx::client_session。 然后,您可以使用以下任一 API 来执行ACID 事务:
Callback API :管理ACID 事务生命周期并自动包含错误处理逻辑的高级API 。
核心API :低级API ,允许您管理ACID 事务的生命周期并实现自定义错误处理逻辑。
回调 API
使用回调API以允许MongoDB C++驱动程序管理ACID 事务的生命周期。 要实现此API,请对 mongocxx::client_session 调用 with_transaction() 方法,并传入回调函数,指定要运行的操作序列。 with_transaction() 方法启动ACID 事务,执行回调函数,并提交ACID 事务或结束ACID 事务(如果遇到错误)。 如果ACID 事务遇到 TransientTransactionError 或 UnknownTransactionCommitResult 错误,则 with_transaction() 方法会重新运行ACID 事务。
以下代码使用 Callback API执行ACID 事务,将文档插入到 sample_mflix数据库的 movies 和 comments 集合中。 此代码执行以下操作:
使用
start_session()方法从客户端启动会话。定义回调函数,指定ACID 事务期间要执行的操作。
调用
with_transaction()方法来管理ACID 事务,并将回调函数和选项对象作为参数传递。
// Establish a connection to the MongoDB deployment mongocxx::instance instance{}; mongocxx::client client(mongocxx::uri{"<connectionString>"}); // Define database and collection variables auto db = client["sample_mflix"]; auto movies_collection = db["movies"]; auto comments_collection = db["comments"]; // Define a callback specifying the sequence of operations to perform during the transaction mongocxx::client_session::with_transaction_cb callback = [&](mongocxx::client_session* session) { // Important:: You must pass the session to the operations. movies_collection.insert_one(*session, make_document(kvp("title", "Parasite"))); comments_collection.insert_one(*session, make_document(kvp("name", "Anjali Patel"), kvp("text", "This is my new favorite movie!"))); }; // Define an options instance to explicitly set the write concern for the transaction operations mongocxx::options::transaction opts; mongocxx::write_concern wc; wc.acknowledge_level(mongocxx::write_concern::level::k_majority); opts.write_concern(wc); // Start a client session auto session = client.start_session(); try { // Start a transaction, execute the operations in the callback function, and commit the results session.with_transaction(callback, opts); } catch (const mongocxx::exception& e) { std::cout << "An exception occurred: " << e.what() << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS;
Core API
使用 Core API管理ACID 事务的生命周期。 要实现此API,您必须显式调用 mongocxx::client_session 接口中的方法来启动ACID 事务、提交活动ACID 事务以及在发生错误时结束ACID 事务。 Core API不会自动包含错误处理逻辑,而是允许您对包括 TransientTransactionError 和 UnknownTransactionCommitResult 在内的错误实现自定义处理逻辑。
下表描述了 mongocxx::client_session 接口提供的 Core API方法:
方法 | 说明 |
|---|---|
| 在当前客户端会话上启动新ACID 事务。接受可选的 |
| 在当前客户端会话上提交ACID 事务事务。如果选项配置错误、存在网络或其他暂时性故障或者存在其他错误(例如会话中没有正在进行的ACID |
| 结束当前客户端会话上的ACID 事务。如果选项配置错误或存在其他错误(例如会话中没有正在进行的ACID |
提示
mongocxx::client_session 类还提供检索和修改会话属性的方法。要学习;了解更多信息,请参阅API文档中的 mongocxx::client_session。
以下代码使用 Core API执行ACID 事务,将文档插入到 sample_mflix数据库的 movies 和 comments 集合中。 此代码执行以下操作:
使用
start_session()方法从客户端启动会话。调用
start_transaction()方法启动ACID 事务,并将选项对象作为参数传入。运行操作以将文档插入到
sample_mflix数据库的集合中,并将活动会话传递给每个操作。 如果操作遇到错误,整个ACID 事务将中止。 如果错误具有标签TransientTransactionError,则会重试ACID 事务。使用
commit_transaction()方法提交ACID 事务。 如果提交遇到标签为UnknownTransactionCommitResult的错误,则会重试提交。
// Establish a connection to the MongoDB deployment mongocxx::instance instance{}; mongocxx::client client(mongocxx::uri{"<connectionString>"}); // Runs the txn_func and retries if TransientTransactionError occurs using transaction_func = std::function<void(mongocxx::client_session& session)>; auto run_with_retry = [](transaction_func txn_func, mongocxx::client_session& session) { while (true) { try { txn_func(session); // performs transaction. break; } catch (const mongocxx::operation_exception& oe) { std::cout << "Transaction aborted. Caught exception during transaction." << std::endl; // If transient error, retry the whole transaction. if (oe.has_error_label("TransientTransactionError")) { std::cout << "TransientTransactionError, retrying transaction..." << std::endl; continue; } else { throw oe; } } } }; // Commits the active transaction and retries commit if UnknownTransactionCommitResult occurs auto commit_with_retry = [](mongocxx::client_session& session) { while (true) { try { session.commit_transaction(); // Uses write concern set at transaction start. std::cout << "Transaction committed." << std::endl; break; } catch (const mongocxx::operation_exception& oe) { // Can retry commit if (oe.has_error_label("UnknownTransactionCommitResult")) { std::cout << "UnknownTransactionCommitResult, retrying commit..." << std::endl; continue; } else { std::cout << "Error during commit..." << std::endl; throw oe; } } } }; auto txn_func = [&](mongocxx::client_session& session) { auto& client = session.client(); // Define database and collection variables auto db = client["sample_mflix"]; auto movies_collection = db["movies"]; auto comments_collection = db["comments"]; // Define an options instance to explicitly set the write concern for the transaction operations mongocxx::options::transaction opts; mongocxx::write_concern wc; wc.acknowledge_level(mongocxx::write_concern::level::k_majority); opts.write_concern(wc); session.start_transaction(opts); // Attempt to insert documents into database collections try { movies_collection.insert_one(session, make_document(kvp("title", "Parasite"))); comments_collection.insert_one(session, make_document(kvp("name", "Anjali Patel"), kvp("text", "This is my new favorite movie!"))); } catch (const mongocxx::operation_exception& oe) { std::cout << "Caught exception during transaction, aborting." << std::endl; session.abort_transaction(); throw oe; } commit_with_retry(session); }; // Start a client session auto session = client.start_session(); try { run_with_retry(txn_func, session); } catch (const mongocxx::operation_exception& oe) { // Do something with error throw oe; }
更多信息
要进一步学习;了解本指南中讨论的概念,请参阅MongoDB Server手册中的以下页面:
要学习;了解有关ACID合规性的更多信息,请参阅MongoDB网站上的《数据库管理系统中的ACID属性》指南。
要了解有关插入操作的更多信息,请参阅插入文档指南。
API 文档
要进一步了解本指南所讨论的任何类型或方法,请参阅以下 API 文档: