对于 AI 代理:可在 https://www.mongodb.com/zh-cn/docs/llms.txt 获取文档索引—通过在任何 URL 路径后添加 .md 可获取所有页面的 Markdown 版本。
Docs 菜单

事务

在本指南中,您可以学习如何使用 MongoDB Go Driver 执行事务。事务允许您运行一系列操作,这些操作在提交事务之前不会更改任何数据。如果事务中的任何操作返回错误,驱动程序就会取消事务,并在所有数据更改变得可见之前予以丢弃。

在 MongoDB 中,事务在逻辑会话中运行。会话是一组要按顺序运行的相关读取或写入操作。会话可以实现一组操作的因果一致性,或支持在 ACID 事务中执行操作。MongoDB 保证事务操作中涉及的数据保持一致,即使操作遇到意外错误。

使用 Go 驱动程序时,您可以从 Client 实例创建一个新会话,并将其类型定义为 Session。我们建议您将客户端重复用于多个会话和事务,而不是每次都实例化新客户端。

警告

仅应将 Session 与创建它的 Client(或关联的 Database 或 Collection)一起使用。将 Session 与其他 Client 一起使用会导致操作错误。

警告

Session 的实现对于并发使用多个 goroutine 是不安全的。

在客户端上使用 StartSession() 方法启动会话后,可以使用返回的 Session 提供的设立集修改会话状态。 下表描述了这些方法:

方法
说明

StartTransaction()

在该会话上启动使用给定选项配置的新事务。如果会话已存在正在进行的事务,则返回错误。要了解有关该方法的更多信息,请参阅服务器手册中的startTransaction() 页面。

参数:TransactionOptions
返回类型:error

AbortTransaction()

终止此会话的活动事务。如果会话中没有活动事务,或事务已提交或结束,则返回错误。要了解有关此方法的更多信息,请参阅服务器手册中的 abortTransaction() 页面。

参数:Context
返回类型:error

CommitTransaction()

提交此会话的活动事务。如果此会话没有活动事务或事务已结束,则返回错误。要了解有关此方法的更多信息,请参阅服务器手册中的 commitTransaction() 页面。

CommitTransaction() 方法是幂等函数,这意味着您可以多次尝试提交事务,而无需在首次成功提交后更改数据。事务可以成功,但会返回带有 UnknownTransactionCommitResult 标签的错误。如果您在收到此错误后重新运行 CommitTransaction() 方法,重复尝试不会更改您的数据。


参数:Context
返回类型:error

WithTransaction()

在此会话上启动事务并运行 fn 回调。

参数:Context、fn func(ctx SessionContext)、TransactionOptions
返回类型:any、error

EndSession()

终止所有现有事务并关闭会话。

参数:Context
返回类型:无

Session还提供检索会话属性和修改可变会话属性的方法。 查看API文档以学习;了解有关这些方法的更多信息。

您可以在会话级别和事务级别设置选项,以自定义驱动程序执行事务的方式。以下步骤描述了如何为给定 Session 内运行的所有事务设置选项:

  1. 创建一个 TransactionOptions 实例。您可为给定会话中运行的所有事务指定写关注、读关注和读取偏好等选项。

  2. 通过调用 SetDefaultTransactionOptions() 方法并将 TransactionOptions 实例作为参数传递,创建 SessionOptions 实例。

    您还可以在您的 SessionOptions 实例中指定其他会话选项,例如因果一致性。

  3. 将 SessionOptions 实例传递给 client.StartSession() 方法。

以下代码指定会话和事务选项,然后创建具有这些选项的会话:

txnOpts := options.Transaction().SetReadConcern(readconcern.Majority())
sessOpts := options.Session().SetDefaultTransactionOptions(txnOpts)
session, err := client.StartSession(sessOpts)
if err != nil {
return err
}

注意

不支持并行操作

Go 驱动程序不支持在单个事务中执行并行操作。

如果您的应用程序连接到 MongoDB Server v8.0 或更高版本,您可以使用客户端批量写入功能在单个事务中对多个命名空间执行写入操作。要了解更多信息,请参阅批量操作指南中的客户端批量写入部分。

本部分中的示例使用以下 Book 结构作为要插入到集合中的文档的数据模型:

type Book struct {
Title string `bson:"title"`
Author string `bson:"author"`
}

以下示例显示如何通过以下步骤创建会话、创建事务以及提交多文档插入操作:

  1. 使用 StartSession() 方法从客户端创建会话。

  2. 使用 WithTransaction() 方法启动事务。

  3. 插入多个文档。WithTransaction() 方法执行插入并提交事务。如果任何操作导致错误,WithTransaction() 将处理取消事务。

  4. 使用 EndSession() 方法关闭ACID 事务和会话。

wc := writeconcern.Majority()
txnOptions := options.Transaction().SetWriteConcern(wc)
// Starts a session on the client
session, err := client.StartSession()
if err != nil {
panic(err)
}
// Defers ending the session after the transaction is committed or ended
defer session.EndSession(context.TODO())
// Inserts multiple documents into a collection within a transaction,
// then commits or ends the transaction
result, err := session.WithTransaction(context.TODO(), func(ctx context.Context) (any, error) {
result, err := coll.InsertMany(ctx, []any{
Book{Title: "The Bluest Eye", Author: "Toni Morrison"},
Book{Title: "Sula", Author: "Toni Morrison"},
Book{Title: "Song of Solomon", Author: "Toni Morrison"},
})
return result, err
}, txnOptions)

如果需要更好地控制事务,可以手动创建、提交和结束事务。此示例演示如何执行以下操作:

  1. 使用 StartSession() 方法从客户端创建会话。

  2. 使用 StartTransaction() 方法启动事务。

  3. 在ACID 事务中插入多个文档。

  4. 使用 CommitTransaction() 方法提交事务。

  5. 使用 EndSession() 方法结束会话。

wc := writeconcern.Majority()
txnOptions := options.Transaction().SetWriteConcern(wc)
// Starts a session on the client
session, err := client.StartSession()
if err != nil {
panic(err)
}
// Defers ending the session after the transaction is committed or ended
defer session.EndSession(context.TODO())
err = mongo.WithSession(context.TODO(), session, func(ctx context.Context) error {
if err = session.StartTransaction(txnOptions); err != nil {
return err
}
docs := []any{
Book{Title: "The Year of Magical Thinking", Author: "Joan Didion"},
Book{Title: "Play It As It Lays", Author: "Joan Didion"},
Book{Title: "The White Album", Author: "Joan Didion"},
}
result, err := coll.InsertMany(ctx, docs)
if err != nil {
return err
}
if err = session.CommitTransaction(ctx); err != nil {
return err
}
fmt.Println(result.InsertedIDs)
return nil
})
if err != nil {
if err := session.AbortTransaction(context.TODO()); err != nil {
panic(err)
}
panic(err)
}

有关插入操作的更多信息,请参阅插入文档基础知识页面。

有关在 Go 驱动程序中指定写关注的更多信息,请参阅写关注。

有关在 Go 驱动程序中使用会话和事务的更多示例,请参阅关于多文档 ACID 事务的开发者博客文章。

要进一步了解本指南所讨论的任何类型或方法,请参阅以下 API 文档: