首页 > 解决方案 > 当 Golang 程序在进行数据库事务时终止时,它可以回滚吗?

问题描述

假设 MariaDB 兼容数据库 (AWS Aurora RDS) 具有默认设置,其中包括启用自动提交,如果 Lambda 函数在执行需要超过五秒的事务三秒终止,例如

  tx, err := h.db.Begin()
  if err != nil {
      log.WithError(err).Error("failed to start transaction")
  }
  res, execErr := tx.Exec(fmt.Sprintf("UPDATE testtable SET val = %d WHERE id = 1; SELECT SLEEP(5.5);", time.Now().Unix()))
  if execErr != nil {
      log.WithError(err).Error("rolling back")
      err = tx.Rollback()
      if err != nil {
          log.WithError(err).Error("failed to roll back")
      }
  }
  if err := tx.Commit(); err != nil {
      log.WithError(err).Error("failed to commit")
  }

人们会假设结果是什么?交易不会被提交?

我正在使用 Go MySQL 驱动程序 v1.3.0-84-g6be42e0 顺便说一句。我还制作了一个视频来展示我的结果,但我想知道它是否正确,因为我不明白驱动程序如何执行回滚

标签: mysqlgotransactions

解决方案


事务 SQL 不提交。我相信 <= 5s 超时会发生什么:

  1. 开始交易
  2. 同步执行 SQL
  3. MySQLd 通过TCP FIN检测到客户端断开连接
  4. MySQLd 自动回滚,因为它没有看到 Commit

来自Golang MySQL 驱动程序维护者之一的推文回复:

当 Go 客户端终止时,mysqld 收到 TCP FIN 数据包。
即使 Go 客户端不干净地退出,操作系统(Linux 内核等)也会发送 TCP FIN 或 TCP RST。所以mysqld可以知道客户端已经消失了。

— INADA Naoki (@methane) 2018 年 12 月 4 日

推荐阅读