如何在 Golang 中使用事务?

大哥大姐们请说明下,如何在 Golang 中使用事务?
最新回答
雨不眠的下

2026-01-19 12:01:59

在 Go 语言中,使用事务(Transaction)可以确保一组数据库操作要么全部成功,要么全部失败,从而维护数据的一致性。以下是详细的使用方法和示例:

1. 事务的基本流程

在 Go 中,事务通过 database/sql 包中的 Tx 类型实现。基本步骤如下:

  1. 开启事务:使用 db.Begin() 启动事务。
  2. 执行操作:在事务中执行查询、更新等操作(使用 tx.QueryRow()、tx.Exec() 等)。
  3. 提交或回滚:

    所有操作成功时,调用 tx.Commit() 提交事务。

    任一操作失败时,调用 tx.Rollback() 回滚事务。

2. 代码示例示例 1:基础事务操作tx, err := db.Begin()if err != nil { return fmt.Errorf("failed to begin transaction: %w", err)}// 执行查询和更新var id, version intif err := tx.QueryRow("SELECT id, version FROM example WHERE name = ?", "foo").Scan(&id, &version); err != nil { tx.Rollback() // 显式回滚 return fmt.Errorf("failed to select: %w", err)}result, err := tx.Exec("UPDATE example SET version = ? WHERE id = ? AND version = ?", version+1, id, version)if err != nil { tx.Rollback() return fmt.Errorf("failed to update: %w", err)}// 提交事务if err := tx.Commit(); err != nil { // 提交失败时尝试回滚 if rbErr := tx.Rollback(); rbErr != nil { return fmt.Errorf("failed to rollback: %w (original error: %v)", rbErr, err) } return fmt.Errorf("failed to commit: %w", err)}示例 2:实战案例(库存与订单)

以下是一个电商系统中扣减库存并创建订单的事务示例:

func CreateOrder(db *sql.DB, productID, quantity int) error { tx, err := db.Begin() if err != nil { return fmt.Errorf("failed to begin transaction: %w", err) } // 检查库存 var stock int if err := tx.QueryRow("SELECT stock FROM products WHERE id = ?", productID).Scan(&stock); err != nil { tx.Rollback() return fmt.Errorf("failed to check stock: %w", err) } if stock < quantity { tx.Rollback() return fmt.Errorf("insufficient stock") } // 扣减库存 if _, err := tx.Exec("UPDATE products SET stock = stock - ? WHERE id = ?", quantity, productID); err != nil { tx.Rollback() return fmt.Errorf("failed to update stock: %w", err) } // 创建订单 if _, err := tx.Exec("INSERT INTO orders (product_id, quantity) VALUES (?, ?)", productID, quantity); err != nil { tx.Rollback() return fmt.Errorf("failed to create order: %w", err) } // 提交事务 if err := tx.Commit(); err != nil { tx.Rollback() return fmt.Errorf("failed to commit: %w", err) } return nil}3. 关键注意事项
  1. 错误处理:

    每次操作后需检查错误,失败时立即回滚。

    提交失败时(如网络中断),需额外处理回滚逻辑。

  2. 并发控制:

    事务通过数据库的锁机制(如行锁、表锁)保证并发操作的一致性。例如,SELECT ... FOR UPDATE 可锁定行避免竞争。

  3. 性能影响:

    长时间运行的事务会占用数据库连接和锁,需尽快提交或回滚。

  4. 嵌套事务:

    Go 的 database/sql 不支持嵌套事务。如需模拟,可通过保存点(Savepoints)实现(部分数据库支持)。

4. 高级用法使用 context 控制超时ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)defer cancel()tx, err := db.BeginTx(ctx, nil)if err != nil { return fmt.Errorf("failed to begin: %w", err)}// 后续操作...保存点(Savepoints)

部分数据库(如 PostgreSQL)支持保存点,允许部分回滚:

_, err = tx.Exec("SAVEPOINT my_savepoint")// 操作失败时回滚到保存点_, err = tx.Exec("ROLLBACK TO SAVEPOINT my_savepoint")总结
  • 事务的核心:确保操作的原子性,避免部分失败导致数据不一致。
  • 典型场景:多表更新、库存扣减、资金转账等。
  • 最佳实践:及时处理错误、合理设置超时、避免长时间持有事务。

通过合理使用事务,可以显著提升应用的可靠性和数据完整性。