mysql菜鸟教程

专栏导航

15.2 开始事务:START TRANSACTION

在上一节中,我们学习了事务的基本概念和 ACID 原则。现在,让我们亲自动手,学习如何在 MySQL 中开始一个事务。掌握 START TRANSACTION 命令是使用事务的第一步,它将开启一个逻辑工作单元,之后的所有 SQL 语句都将被视为同一个事务的一部分,直到你明确地提交或回滚。

一、开始事务的基本语法

在 MySQL 中,开始事务有三种等效的方式:

START TRANSACTION;
或者
BEGIN;
或者
BEGIN WORK;

这三种写法作用完全相同,都是显式地开始一个新的事务。通常推荐使用 START TRANSACTION,因为它的语义最清晰。

示例:开始一个简单的转账事务

START TRANSACTION;

UPDATE accounts SET balance = balance - 100 WHERE account_id = 'A';
UPDATE accounts SET balance = balance + 100 WHERE account_id = 'B';

COMMIT;  -- 提交事务,使修改永久生效

在这个例子中,从 START TRANSACTION 到 COMMIT 之间的两条 UPDATE 语句构成了一个原子操作。

二、自动提交模式(AUTOCOMMIT)

在理解显式事务之前,需要先了解 MySQL 的默认行为。默认情况下,MySQL 以自动提交(autocommit) 模式运行。这意味着每条单独的 SQL 语句都会在其执行完成后自动提交,相当于每条语句都隐含地包裹在事务中。

你可以通过以下命令查看当前的自动提交状态:

SHOW VARIABLES LIKE 'autocommit';

默认值是 ON(1)。

关闭自动提交

如果你想隐式地开始事务(即不显式写 START TRANSACTION,但希望多条语句作为一个事务),可以临时关闭自动提交:

SET AUTOCOMMIT = 0;  -- 关闭自动提交
-- 之后的所有语句都属于同一事务,直到手动 COMMIT 或 ROLLBACK
UPDATE accounts ...;
UPDATE accounts ...;
COMMIT;              -- 提交后,事务结束,但 autocommit 仍为 0

当 AUTOCOMMIT = 0 时,你执行的每一条 SQL 都不会立即提交,而是累积起来,直到你执行 COMMIT 或 ROLLBACK。执行 COMMIT 后,下一个事务会自动开始(仍然不自动提交)。

注意:关闭自动提交后,如果忘记提交,修改可能不会持久化,且会持有锁,容易造成死锁。因此,对于初学者,建议显式使用 START TRANSACTION 来明确事务边界。

三、提交与回滚

事务开始后,你可以通过以下两种方式结束它:

  • COMMIT:提交事务,使所有修改永久生效。

  • ROLLBACK:回滚事务,撤销所有修改,数据库恢复到事务开始前的状态。

示例:带错误处理的事务

假设我们在转账过程中检测到余额不足,需要回滚整个事务:

START TRANSACTION;

SELECT balance FROM accounts WHERE account_id = 'A' FOR UPDATE;  -- 加锁检查

-- 假设应用程序逻辑判断余额不足
ROLLBACK;  -- 撤销任何修改(如果有)

如果没有显式提交或回滚,事务会一直保持打开状态,直到会话结束(此时会自动回滚)。

四、保存点(SAVEPOINT)

有时候,你可能不想回滚整个事务,而只想回滚到事务中的某个中间点。这时可以使用保存点(SAVEPOINT)

语法

SAVEPOINT 保存点名称;
ROLLBACK TO [SAVEPOINT] 保存点名称;
RELEASE SAVEPOINT 保存点名称;  -- 删除保存点

示例:在复杂事务中使用保存点

START TRANSACTION;

UPDATE accounts SET balance = balance - 100 WHERE account_id = 'A';
SAVEPOINT after_deduct;

UPDATE accounts SET balance = balance + 100 WHERE account_id = 'B';

-- 假设发现 B 账户有问题,但 A 账户扣款没问题,只想回滚到 after_deduct 点
ROLLBACK TO SAVEPOINT after_deduct;

-- 现在事务处于 after_deduct 点的状态,A 已扣款,B 未加款
-- 可以继续执行其他操作,最后提交
UPDATE accounts SET balance = balance + 100 WHERE account_id = 'C';
COMMIT;

重要

  • 保存点只能在事务内部使用。

  • 回滚到保存点不会结束事务,只是撤销了保存点之后的修改。

  • 事务提交或整体回滚后,所有保存点自动释放。

五、隐式提交的语句

有些 SQL 语句在执行前会隐式地提交当前事务,就像执行了一个 COMMIT 一样。这些语句通常包括:

  • 数据定义语言(DDL)语句:CREATEALTERDROPTRUNCATERENAME 等。

  • 权限管理语句:GRANTREVOKESET PASSWORD 等。

  • 锁表语句:LOCK TABLESUNLOCK TABLES

  • 管理语句:ANALYZE TABLECACHE INDEXCHECK TABLEOPTIMIZE TABLEREPAIR TABLE 等。

因此,在一个显式事务中,如果你执行了 DDL 语句,前面的修改会被自动提交,后续的操作将属于一个新的事务。这可能会破坏事务的原子性,所以事务中应尽量避免混用 DDL。

六、综合示例:完整的银行转账事务

-- 关闭自动提交(可选,但为了清晰我们显式开始事务)
SET AUTOCOMMIT = 0;

START TRANSACTION;

-- 检查账户 A 余额是否足够
SELECT @bal := balance FROM accounts WHERE account_id = 'A' FOR UPDATE;

IF @bal >= 100 THEN
    UPDATE accounts SET balance = balance - 100 WHERE account_id = 'A';
    UPDATE accounts SET balance = balance + 100 WHERE account_id = 'B';
    COMMIT;
    SELECT '转账成功' AS result;
ELSE
    ROLLBACK;
    SELECT '余额不足' AS result;
END IF;

注意:上面的 IF 是伪代码,实际需要在存储过程或应用程序中实现。在纯 SQL 中,可以通过条件判断结合事务,但通常是在应用程序中编写逻辑。

七、小结

  • 使用 START TRANSACTION(或 BEGIN)显式开始一个事务。

  • 事务开始后,后续的 SQL 语句属于同一个工作单元,直到 COMMIT 或 ROLLBACK

  • MySQL 默认是自动提交模式,可以通过 SET AUTOCOMMIT = 0 关闭,但显式事务更清晰。

  • 保存点允许部分回滚,增加了事务的灵活性。

  • 注意某些 DDL 语句会隐式提交当前事务,应避免在事务中间使用。

掌握开始事务的方法后,下一节我们将学习如何正确地提交和回滚事务,以及在并发环境下可能遇到的各种问题。


所有评论

关于我 备案号:蜀ICP备2023042032号-1