Golang 使用事务的简单实践

 更新时间:2025年11月02日 09:38:42   作者:lastHertz  
事务是保证数据一致性的重要手段,在Golang项目中,我们可以通过事务管理器或GORM的来简化事务的使用,本文就来详细的介绍一下Golang 使用事务的实现示例,感兴趣的可以了解一下

在实际业务开发中,事务(Transaction)是保证数据一致性的重要手段。比如:

  • 用户注册时,需要同时写入用户表和日志表;
  • 订单支付时,需要同时扣减库存和生成支付流水;
  • 转账时,需要同时扣减账户 A 的余额并增加账户 B 的余额。

这些操作必须 要么全部成功,要么全部失败,否则就会导致数据不一致。本文将结合 Golang 的示例代码,介绍如何在项目中优雅地使用事务。

一、事务的基本概念

事务具备 ACID 四大特性:

  • A(Atomicity,原子性):事务中的操作要么全部成功,要么全部失败。
  • C(Consistency,一致性):事务执行前后,数据必须保持一致。
  • I(Isolation,隔离性):多个事务之间相互独立,互不干扰。
  • D(Durability,持久性):事务一旦提交,数据就会被永久保存。

二、事务的使用示例

// 需要使用事务的方法
func (s *userService) funcName(ctx context.Context, req *v1.Req) (*v1.RespData, error) {
    // 获取事务的最终结果
    err := s.tm.Transaction(ctx, func(ctx context.Context) error {
        // 内部写相关的原子性数据库操作
        // 如果任意操作报错,将触发回滚,恢复之前的状态

        // 调用数据层方法 repository
        // repository.CreateUser(ctx, req)
        // repository.CreateLog(ctx, req)

        // 所有操作均无错误,正常退出
        return nil
    })

    // 如果事务中存在错误,所有操作都会被回滚
    if err != nil {
        return nil, err
    }

    // 没有触发事务报错,正常返回结果
    return &v1.RespData{}, nil
}

三、结合 GORM 使用事务

如果你使用的是 GORM,事务的写法会更简洁:

func (s *userService) CreateOrder(ctx context.Context, req *v1.OrderReq) error {
    return s.db.Transaction(func(tx *gorm.DB) error {
        // 创建订单
        if err := tx.Create(&Order{UserID: req.UserID, Amount: req.Amount}).Error; err != nil {
            return err // 返回错误会触发回滚
        }

        // 扣减库存
        if err := tx.Model(&Product{}).
            Where("id = ? AND stock >= ?", req.ProductID, req.Quantity).
            Update("stock", gorm.Expr("stock - ?", req.Quantity)).Error; err != nil {
            return err
        }

        // 写入日志
        if err := tx.Create(&Log{Action: "create_order", UserID: req.UserID}).Error; err != nil {
            return err
        }

        // 所有操作成功,事务提交
        return nil
    })
}

四、事务的应用场景

  • 用户注册:写入用户表 + 写入用户详情表 + 写入日志表。
  • 订单支付:扣减库存 + 生成订单记录 + 写入支付流水。
  • 资金转账:账户 A 扣款 + 账户 B 加款 + 生成转账记录。

五、最佳实践

  1. 事务粒度要小:只包含必要的数据库操作,避免长时间占用连接。
  2. 错误处理要及时:一旦事务中出现错误,应立即返回,触发回滚。
  3. 避免耗时操作:不要在事务中调用外部 API 或执行复杂计算。
  4. 封装事务逻辑:在服务层统一封装事务,减少重复代码。
  5. 结合 Context:在事务中传递 context.Context,方便控制超时和取消。

六、常见问题 FAQ

Q1:事务中如何传递上下文(Context)?
在事务回调函数中继续传递 ctx,保证日志、超时控制等功能生效。例如:

s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
    return tx.Create(&User{Name: "Tom"}).Error
})

Q2:如何在事务中调用多个 repository?
只需将事务对象 tx 传递给 repository 方法即可:

func (r *UserRepo) Create(ctx context.Context, tx *gorm.DB, user *User) error {
    return tx.WithContext(ctx).Create(user).Error
}

这样可以保证所有 repository 操作都在同一个事务中。

Q3:事务中能否执行外部 API 调用?
不推荐。外部 API 调用可能耗时较长,导致事务长时间占用数据库连接,影响性能。建议先执行事务,再调用外部服务,或通过消息队列解耦。

Q4:如何处理事务嵌套?
GORM 默认不支持真正的嵌套事务,但可以使用 SavePointRollbackTo 来模拟:

tx.SavePoint("sp1")
// ...
tx.RollbackTo("sp1")

七、总结

事务是保证数据一致性的重要手段。在 Golang 项目中,我们可以通过事务管理器或 GORM 的 db.Transaction 来简化事务的使用。

只要遵循 小粒度、快执行、及时回滚 的原则,就能在项目中高效、安全地使用事务。

到此这篇关于Golang 使用事务的简单实践的文章就介绍到这了,更多相关Golang 事务内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 详解golang开发中select多路选择

    详解golang开发中select多路选择

    这篇文章主要介绍了golang开发中select多路选择,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-09-09
  • Go语言七篇入门教程七GC垃圾回收三色标记

    Go语言七篇入门教程七GC垃圾回收三色标记

    这篇文章主要为大家介绍了Go语言教程关于GC垃圾回收三色标记的示例详解,本篇文章是Go语言七篇入门教程系列文章,有需要的朋友可以借鉴参考下,希望能够有所帮助
    2021-11-11
  • Go 语言中的指针的使用

    Go 语言中的指针的使用

    在Go语言中,指针是存储另一变量内存地址的变量,通过&操作符获取变量地址,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-09-09
  • go语言用八百行代码实现一个JSON解析器

    go语言用八百行代码实现一个JSON解析器

    这篇文章主要为大家介绍了go语言用八百行代码实现一个JSON解析器实例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-07-07
  • go语言中匿名函数的作用域陷阱详解

    go语言中匿名函数的作用域陷阱详解

    GO语言的匿名函数(anonymous function),其实就是闭包.是指不需要定义函数名的一种函数实现方式,下面这篇文章主要给大家介绍了关于go语言中匿名函数作用域陷阱的相关资料,需要的朋友可以参考下
    2022-05-05
  • Golang垃圾回收器执行链路详细分析

    Golang垃圾回收器执行链路详细分析

    垃圾回收简称GC,就是对程序中不再使用的内存资源进行自动回收释放的操作,这篇文章主要介绍了Golang垃圾回收器执行链路详细分析的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2025-12-12
  • GO语言学习之语句块的实现

    GO语言学习之语句块的实现

    本文主要介绍了GO语言学习之语句块的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-06-06
  • 详解Go语言单元测试中如何解决MySQL存储依赖问题

    详解Go语言单元测试中如何解决MySQL存储依赖问题

    MySQL 存储就是一个非常常见的外部依赖,这篇文章主要来和大家一起探讨在 Go 语言中编写单元测试时,如何解决 MySQL 存储依赖,需要的可以参考一下
    2023-07-07
  • Go实现自动解压缩包以及读取docx/doc文件内容详解

    Go实现自动解压缩包以及读取docx/doc文件内容详解

    在开发过程中,我们常常需要处理压缩包和文档文件。本文将介绍如何使用Go语言自动解压缩包和读取docx/doc文件,需要的可以参考一下
    2023-03-03
  • GO的基础知识扫盲注意事项

    GO的基础知识扫盲注意事项

    这篇文章主要介绍了GO的基础知识注意事项,本文是GO语言小白的扫盲文,主要讲解了go语言的基本知识,GO程序目录结构,GO程序包的导入与别名运用,GO内置关键字,GO注释方法需要的朋友可以参考下
    2022-12-12

最新评论