添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
耍酷的墨镜  ·  Spring Data MongoDB ...·  2 周前    · 
讲道义的鞭炮  ·  springboot mongodb ...·  2 周前    · 
有情有义的橙子  ·  birdben·  2 周前    · 
年轻有为的熊猫  ·  Job Search·  3 月前    · 
谦和的皮带  ·  腾讯体育-社区·  4 月前    · 
粗眉毛的蚂蚁  ·  《交易沦陷》by ...·  10 月前    · 
近视的金鱼  ·  OpenWrt Support for ...·  1 年前    · 

MongoDB事务

事务

在MongoDB中,对单个文档的操作是原子的。由于可以在单个文档结构中使用内嵌文档和数组来获得数据之间的关系,而不必跨多个文档和集合进行范式化,所以这种单文档原子性避免了许多实际场景中对多文档事务的需求。

对于那些需要对多个文档(在单个或多个集合中)进行原子性读写的场景,MongoDB支持多文档事务。而使用分布式事务,事务可以跨多个操作、集合、数据库、文档和分片使用。

事务API


➤ 使用右上角的 Select your language 下拉菜单来设置以下示例的语言。


此示例突出显示了事务API的关键组件。

该示例使用新的回调API来进行事务处理,其中涉及启动事务、执行指定的操作并提交(或在出错时中止)。新的回调API还包含针对 TransientTransactionError UnknownTransactionCommitResult 提交错误的重试逻辑。

重要

  • 推荐 。使用针对MongoDB部署版本更新的MongoDB驱动程序。对于MongoDB 4.2部署(副本集和分片集群上的事务,客户端 必须 使用为MongoDB 4.2更新的MongoDB驱动程序。

  • 使用驱动程序时,事务中的每个操作 必须 与会话相关联(即将会话传递给每个操作)。

  • 事务中的操作使用 事务级读关注 ,[事务级写关注](https ://docs.mongodb.com/manual/core/transactions/#std-label-transactions-write-concern)和 事务级读偏好

  • 在MongoDB 4.2及更早版本中,你无法在事务中创建集合。如果在事务内部运行会导致文档插入的写操作(例如 insert 或带有 upsert: true 的更新操作),必须在 已存在 的集合上才能执行。

  • 从MongoDB 4.4开始,你可以隐式或显式地在事务中创建集合。但是,必须使用针对4.4更新的MongoDB驱动程序。有关详细信息,请参阅 在事务中创建集合和索引

static bool
with_transaction_example (bson_error_t *error)
   mongoc_client_t *client = NULL;
   mongoc_write_concern_t *wc = NULL;
   mongoc_read_concern_t *rc = NULL;
   mongoc_read_prefs_t *rp = 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; e.g.
    * uri_repl = "mongodb://mongodb0.example.com:27017,mongodb1.example.com:" \
    *    "27017/?replicaSet=myRepl";
    * client = test_framework_client_new (uri_repl);
    * For a sharded cluster, connect to the mongos instances; e.g.
    * uri_sharded =
    * "mongodb://mongos0.example.com:27017,mongos1.example.com:27017/";
    * client = test_framework_client_new (uri_sharded);
   client = get_client ();
   /* Prereq: Create collections. */
   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. */
   session = mongoc_client_start_session (client, NULL /* opts */, error);
   if (!session) {
      goto fail;
   /* Step 2: Optional. Define options to use for the transaction. */
   txn_opts = mongoc_transaction_opts_new ();
   rp = mongoc_read_prefs_new (MONGOC_READ_PRIMARY);
   rc = mongoc_read_concern_new ();
   mongoc_read_concern_set_level (rc, MONGOC_READ_CONCERN_LEVEL_LOCAL);
   mongoc_transaction_opts_set_read_prefs (txn_opts, rp);
   mongoc_transaction_opts_set_read_concern (txn_opts, rc);
   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_read_concern_destroy (rc);
   mongoc_read_prefs_destroy (rp);
   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;
   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;
}




    

同样请参阅:

有关 mongo shell中的示例,请参阅 mongo Shell示例

事务和原子性

说明

分布式事务和多文档事务

从MongoDB 4.2开始,这两个术语是同义词。分布式事务是指分片集群和副本集上的多文档事务。从MongoDB 4.2开始,多文档事务(无论是在分片集群上还是副本集上)也称为分布式事务。

对于多文档(在单个或多个集合中)读写上有原子性要求的场景,MongoDB提供了多文档事务支持:

  • 在4.0版本 中,MongoDB支持副本集上的多文档事务。

  • 在4.2版本 中,MongoDB引入了分布式事务,增加了对分片集群上多文档事务的支持,并合并了对副本集上多文档事务的现有支持。

    为了在MongoDB 4.2部署(副本集和分片集群)上使用事务,客户端 必须 使用为MongoDB 4.2更新的MongoDB驱动程序。

Multi-document transactions are atomic (i.e. provide an "all-or-nothing" proposition):

多文档事务是原子的(即提供“全有或全无”的语义):

  • 当事务提交时,事务中所做的所有数据更改都将保存并在事务外部可见。也就是说,事务不会在回滚其他更改时提交其某些更改。

    在事务提交之前,事务中所做的数据更改在事务之外是不可见的。

    然而,当事务写入多个分片时,并非所有外部读取操作都需要等待已提交事务的结果在分片中可见。例如,如果事务已提交并且写入操作1在分片A上可见,但写入操作2在分片B上尚不可见,则外部读关注为 "local" 的读操作可以读取写入操作1的结果,看不到写入操作2。

  • 当事务中止时,事务中所做的所有数据更改都将被丢弃,而不会变得可见。例如,如果事务中的任何操作失败,事务就会中止,并且事务中所做的所有数据更改都将被丢弃,而不会变得可见。

重要

在大多数情况下,多文档事务比单文档写入会产生更大的性能成本,并且多文档事务的可用性不应替代有效的模型设计。对于许多场景, 反范式化数据模型(嵌入文档和数组) 依然会是最适合你的数据和用例。也就是说,对于许多场景,适当地对数据进行建模可以最大限度地减少对多文档事务的需求。

有关其他事务使用注意事项(例如runtime限制和oplog大小限制),另请参阅 生产注意事项

TIP

同样请参阅:

Commit期间的外部读操作

事务和操作

分布式事务可用于跨多个操作、集合、数据库、文档以及从MongoDB 4.2开始可以跨分片。

对于事务:

  • 可以在 现有 集合上指定读/写(CRUD)操作。 有关CRUD操作的列表,请参阅 CRUD操作

  • 当使用 功能兼容版本(fcv) "4.4" 或更高版本时,可以在事务中创建集合和索引。详情请参考 在事务中创建集合和索引

  • 事务中使用的集合可以位于不同的数据库中。

    提示

    你不能在跨分片的写事务中创建新集合。例如,如果你想对一个分片中已存在的集合进行写入且在另外一个不同的分片中隐式地创建集合,那么MongoDB无法在同一事务中执行这两种操作。

  • 你不能写入 capped 集合。(从 MongoDB 4.2 开始)

  • 你不能读/写 config admin local 数据库中的集合。

  • 你不能写 system.* 集合。

  • 你不能返回这类支持操作的查询计划(即 explain )。

  • 对于在事务外部创建的游标,你不能在事务内部调用 getMore

  • 对于在事务内创建的游标,你不能在事务外调用 getMore

  • 从MongoDB 4.2开始,你不能将 killCursors 定义为 事务 中的第一个操作。

有关事务中不支持的操作列表,请参阅 受限操作

提示

在开始事务之前立即创建或删除集合时,如果在事务内访问该集合,注意使用写关注 "majority" 来执行这些创建或删除操作,从而确保事务可以获取到所需要的锁。

提示

同样请参阅:

事务和操作参考

在事务中创建集合和索引

从MongoDB 4.4开始,使用 功能兼容性版本(fcv) "4.4" ,可以在 多文档事务 中创建集合和索引,除非事务是跨分片写入事务。如果使用 "4.2" 或更低版本,事务中 不允许 使用影响数据库目录的操作,例如创建或删除集合和索引。

当在事务内部创建一个集合时:

在事务中创建索引 [[1]](https: //docs.mongodb.com/manual/core/transactions/#footnote-create-existing-index),要创建的索引需满足下面两者之一情况:

  • 一个不存在的集合。集合的创建是作为操作的一部分。

  • 先前在同一事务中创建的新空集合。

[ 1 ]

还可以对现有索引运行 db.collection.createIndex() db.collection.createIndexes() 来检查是否存在。这些操作会成功地返回且不会创建索引。

限制

提示

同样请参阅:

受限制的操作

计数操作

要在事务中执行计数操作,请使用 $count 聚合阶段或带有[ $sum ](https ://docs.mongodb.com/manual/reference/operator/aggregation/sum/#mongodb-group-grp.-sum)表达式的 $group 聚合阶段。

与4.0特性兼容的MongoDB驱动程序提供了一个集合级别的API countDocuments(filter, options) 作为带有 $sum 表达式的[ $group ](https://docs.mongodb.com/manual/reference/operator /aggregation/group/#mongodb-pipeline-pipe.-group)的帮助函数来执行计数。4.0驱动程序已弃用 count() API。

从MongoDB4.0.3开始, mongo shell提供了在 db.collection.countDocuments() 中使用带有[ $sum ](https://docs.mongodb.com/ manual/reference/operator/aggregation/sum/#mongodb-group-grp.-sum)表达式的 $group 来执行计数的帮助函数。

Distinct操作

为了在事务中执行一个distinct操作:

  • 对于未分片的集合,可以使用[ db.collection.distinct() ](https://docs.mongodb.com/manual/reference/method/db.collection.distinct/#mongodb-method-db.collection .distinct)方法/ distinct 命令以及带有 $group 阶段的聚合管道。

  • 对于分片的集合,不能使用 db.collection.distinct() 方法或者 distinct 命令。

    要查找分片集合的不同值,请使用带 $group 阶段的聚合管道作为替代。例如:

    • 替代 db.coll.distinct("x") ,请使用:

      db.coll.aggregate([
         { $group: { _id: null, distinctValues: { $addToSet: "$x" } } },