为什么要推迟回滚?

Lar*_*sen 11 transactions go

我已经开始将 Go 用于 Web 服务并进行了一些数据库交互(惊喜!!!),我找到了这个例子:

tx, err := db.Begin()
if err != nil {
    log.Fatal(err)
}
defer tx.Rollback()
stmt, err := tx.Prepare("INSERT INTO foo VALUES (?)")
if err != nil {
    log.Fatal(err)
}
defer stmt.Close() // danger!
for i := 0; i < 10; i++ {
    _, err = stmt.Exec(i)
    if err != nil {
        log.Fatal(err)
    }
}
err = tx.Commit()
if err != nil {
    log.Fatal(err)
}
// stmt.Close() runs here!
Run Code Online (Sandbox Code Playgroud)

来自http://go-database-sql.org/prepared.html

该示例制定得很好,易于理解。然而,这给我留下了一个悬而未决的问题。为什么defer事务回滚调用?

为什么不做以下事情:

err := tx.Commit()

if err != nil {
    log.Error(err)
    tx.Rollback()
}
Run Code Online (Sandbox Code Playgroud)

defer tx.Rollback()不总是试图回滚?即使tx.Commit()成功了,还是我误解了什么defer

Tom*_*mor 19

重要的是,如果您推迟 tx.Rollback(),即使您提前返回,它也会被调用,并且“技巧”是调用tx.Rollback()已提交的事务实际上不会进行回滚,因为一旦提交了事务,它就是提交并且无法回滚它:) 所以这是一个关于如何保持代码简单的巧妙技巧。

  • 嗨@bosvos,它不会。您可以在`database/sql/sql.go`中查看Rollback()代码。它首先执行“atomic.CompareAndSwapInt32(&amp;tx.done, 0, 1)”,如果交易完成,它会立即返回。 (6认同)
  • 成功提交后的额外回滚是否不会导致到服务器的往返,无论它实际上是 NOP?如果是这种情况,并且有数百个连续的此类(成功)操作,那么这个“技巧”将导致性能下降。 (3认同)

Pet*_*ter 5

这个例子有点误导。它log.Fatal(err)用于错误处理。你通常不会这样做,而是return err. 因此,延迟回滚是为了确保在提前返回的情况下回滚事务。