You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MongoDB独立模式下事务未回滚的原因及解决方法求助

独立模式下MongoDB事务未回滚的原因及解决方法

问题场景

需要在MongoDB中实现多语句事务,达成「要么全部成功,要么全部失败」的效果。当前trxTest数据库的users集合已有如下文档:

trxTest> db.users.find({})
[ { _id: ObjectId("64ad882f451d9a5fc930eeb7"), name: 'werain' } ]

name字段已设置唯一索引,尝试在事务中插入多个文档,最后一个文档的name为werain会触发唯一键冲突。预期最后一个插入失败时,事务回滚前面插入的vikas和sriram文档,但实际执行后这两个文档仍存在于集合中。

问题代码(Go语言)

client, err := mongo.Connect(context.Background(), options.Client().ApplyURI("mongodb://localhost:27017"))
if err != nil {
    panic(err)
}
DB := client.Database("trxTest")
coll1 := DB.Collection("users")

session, err := client.StartSession()
if err != nil {
    panic(err)
}

defer session.EndSession(context.Background())

_, err = session.WithTransaction(context.Background(), func(ctx mongo.SessionContext) (interface{}, error) {
    _, err = coll1.InsertOne(context.Background(), bson.M{"name": "vikas"})
    if err != nil {
        return nil, err
    }

    fmt.Println("vikas is inserted")


    _, err = coll1.InsertOne(context.Background(), bson.M{"name": "sriram"})
    if err != nil {
        return nil, err
    }
    fmt.Println("sriram is inserted")

    fmt.Println("inserting is werain again")
    _, err = coll1.InsertOne(context.Background(), bson.M{"name": "werain"}) // 此处应触发事务失败
    if err != nil {
        return nil, err
    }
    fmt.Println("werain is inserted")
    return nil, nil
}, nil)

if err != nil {
    fmt.Println("got trx error", err)
}

实际执行结果

trxTest> db.users.find({})
[
  { _id: ObjectId("64ad882f451d9a5fc930eeb7"), name: 'werain' },
  { _id: ObjectId("64ad8c65f63f3f2b48f66169"), name: 'vikas' },
  { _id: ObjectId("64ad8c66f63f3f2b48f6616a"), name: 'sriram' }
]

报错信息

vikas is inserted
sriram is inserted
inserting is werain again

got error write exception: write errors: [E11000 duplicate key error collection: trxTest.users index: name_1 dup key: { name: "werain" }]

MongoDB版本信息

db version v6.0.1
Build Info: {
    "version": "6.0.1",
    "gitVersion": "32f0f9c88dc44a2c8073a5bd47cf779d4bfdee6b",
    "modules": [],
    "allocator": "system",
    "environment": {
        "distarch": "aarch64",
        "target_arch": "aarch64"
    }
}

原因分析

核心问题是事务回调内的所有InsertOne操作,传入的上下文是context.Background()而非事务提供的SessionContext(即回调参数ctx)。MongoDB的事务操作必须绑定到事务上下文,否则每个插入都会作为独立的非事务操作执行,不受事务管理,自然不会触发回滚。

解决方法

将事务回调中所有InsertOne调用的上下文参数替换为回调的ctx,修正后的代码如下:

_, err = session.WithTransaction(context.Background(), func(ctx mongo.SessionContext) (interface{}, error) {
    _, err = coll1.InsertOne(ctx, bson.M{"name": "vikas"})
    if err != nil {
        return nil, err
    }

    fmt.Println("vikas is inserted")


    _, err = coll1.InsertOne(ctx, bson.M{"name": "sriram"})
    if err != nil {
        return nil, err
    }
    fmt.Println("sriram is inserted")

    fmt.Println("inserting is werain again")
    _, err = coll1.InsertOne(ctx, bson.M{"name": "werain"}) // 此处触发事务失败
    if err != nil {
        return nil, err
    }
    fmt.Println("werain is inserted")
    return nil, nil
}, nil)

验证结果

修改后执行,当最后一个插入触发唯一键冲突时,事务会自动回滚,users集合中仅保留初始的werain文档,不会出现vikas和sriram。

内容的提问来源于stack exchange,提问作者Vicky

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 07:53:34