mysql菜鸟教程

专栏导航

15.3 提交与回滚:COMMIT和ROLLBACK

在上一节中,我们学习了如何用 START TRANSACTION 开始一个事务。但事务开始后,如何让它生效或取消呢?这就涉及两个关键命令:COMMIT 和 ROLLBACK。它们是事务的终点,决定了事务内所有操作的最终命运。

一、COMMIT:让修改永久生效

COMMIT 语句用于提交当前事务,即将事务中所有未提交的更改永久保存到数据库中。提交后,事务持有的锁会被释放,其他事务可以看到这些更改(取决于隔离级别)。

基本语法

COMMIT;

示例:成功的转账

START TRANSACTION;

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

COMMIT;  -- 两条更新永久生效

执行 COMMIT 后,数据的变化就无法通过 ROLLBACK 撤消了。

二、ROLLBACK:撤销所有更改

ROLLBACK 语句用于回滚当前事务,即撤销事务中所有已执行的修改,将数据库恢复到事务开始之前的状态。回滚后,事务持有的锁也会被释放。

基本语法

ROLLBACK;

示例:余额不足,取消转账

START TRANSACTION;

SELECT @bal := balance FROM accounts WHERE account_id = 'A';

-- 假设在应用程序中检查 @bal,发现小于 100,则执行回滚
ROLLBACK;  -- 任何未提交的修改都被撤销

如果在 ROLLBACK 之前已经执行了一些 UPDATE,这些 UPDATE 的影响会被完全消除。

三、事务的生命周期

一个典型的事务经历以下阶段:

1、开始START TRANSACTION 或 BEGIN

2、操作:执行一条或多条 SQL 语句(INSERTUPDATEDELETE 等)。

3、结束COMMIT 或 ROLLBACK

  • 如果提交,更改永久生效。

  • 如果回滚,更改全部撤销。

4、后续:事务结束后,数据库回到空闲状态。如果 AUTOCOMMIT 为 1,下一条语句会自动开始新事务;如果为 0,则需要再次 START TRANSACTION 或执行语句(会隐式开始新事务)。

四、自动提交模式与显式提交

默认情况下,MySQL 是自动提交模式(AUTOCOMMIT=1)。这意味着每一条 SQL 语句执行后都会自动提交,相当于每条语句都隐含了 COMMIT。要使用多语句事务,必须显式关闭自动提交或使用 START TRANSACTION

查看当前自动提交状态

SHOW VARIABLES LIKE 'autocommit';

在自动提交模式下使用事务

SET AUTOCOMMIT = 0;  -- 关闭自动提交
-- 之后的所有语句不会自动提交,需要手动 COMMIT 或 ROLLBACK
UPDATE accounts ...;
UPDATE accounts ...;
COMMIT;  -- 手动提交

-- 如果要重新开启自动提交
SET AUTOCOMMIT = 1;

更常见的做法是不改变自动提交设置,而是显式使用 START TRANSACTION,这样事务结束后会自动恢复原来的自动提交模式。

START TRANSACTION;
UPDATE ...;
UPDATE ...;
COMMIT;  -- 事务结束,自动提交模式恢复原状

五、结合保存点的部分回滚

ROLLBACK 默认会回滚整个事务。但如果你只想撤销事务的一部分,可以使用保存点SAVEPOINT)。

示例:部分回滚

START TRANSACTION;

INSERT INTO logs(message) VALUES('开始转账');
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 已扣款
ROLLBACK TO SAVEPOINT after_deduct;  -- 撤销对 B 的修改,保留 A 的扣款

-- 可以继续执行其他操作
UPDATE accounts SET balance = balance + 100 WHERE account_id = 'C';
COMMIT;  -- 最终只提交 A 扣款和 C 加款

注意

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

  • 保存点之前的修改依然存在,可以继续操作或最终提交。

  • 执行 COMMIT 或整体 ROLLBACK 后,所有保存点自动释放。

六、注意事项

  1. DDL 语句的隐式提交CREATEALTERDROPTRUNCATE 等数据定义语言(DDL)语句会隐式提交当前事务。这意味着如果你在一个事务中执行了 DDL,之前的修改会被自动提交,且之后无法回滚。因此,事务中应避免混用 DDL。

  2. 事务未提交的后果:如果事务开始后既没有 COMMIT 也没有 ROLLBACK,事务会一直保持打开状态,可能持有锁并阻止其他会话。长时间未提交的事务是性能杀手,务必及时结束事务。

  3. 断开连接时的处理:如果客户端会话在没有显式提交或回滚的情况下断开连接,MySQL 会自动回滚该会话中未提交的事务。

  4. COMMIT 和 ROLLBACK 后的事务状态:执行完 COMMIT 或 ROLLBACK 后,当前事务结束。如果你之前是显式 START TRANSACTION,那么系统回到事务未开始的状态,下一条语句如果是普通 SQL,会遵循当前的自动提交模式。如果想开始另一个事务,需要再次 START TRANSACTION

七、综合示例:带条件判断的转账

在实际应用中,事务往往与业务逻辑结合。下面是一个简化的存储过程示例,演示如何在事务中使用条件回滚:

DELIMITER //

CREATE PROCEDURE Transfer(
    IN from_account VARCHAR(10),
    IN to_account VARCHAR(10),
    IN amount DECIMAL(10,2),
    OUT success BOOLEAN
)
BEGIN
    DECLARE EXIT HANDLER FOR SQLEXCEPTION
    BEGIN
        ROLLBACK;
        SET success = FALSE;
    END;

    START TRANSACTION;

    -- 检查余额是否足够
    IF (SELECT balance FROM accounts WHERE account_id = from_account) < amount THEN
        ROLLBACK;
        SET success = FALSE;
    ELSE
        UPDATE accounts SET balance = balance - amount WHERE account_id = from_account;
        UPDATE accounts SET balance = balance + amount WHERE account_id = to_account;
        COMMIT;
        SET success = TRUE;
    END IF;
END //

DELIMITER ;

调用:

CALL Transfer('A', 'B', 100, @ok);
SELECT @ok;

八、小结

命令

作用

事务状态

COMMIT

提交当前事务,所有更改永久生效

事务结束

ROLLBACK

回滚当前事务,撤销所有更改

事务结束

ROLLBACK TO SAVEPOINT

回滚到指定保存点,但不结束事务

事务继续

  • 提交让修改成为现实,回滚让修改化为泡影。

  • 始终确保事务有明确的终点(COMMIT 或 ROLLBACK),避免长事务。

  • 注意 DDL 的隐式提交,不要在事务中随意使用。

  • 结合保存点可以实现更精细的控制。

掌握了提交与回滚,你就真正掌握了事务的核心操作。下一节我们将讨论不同隔离级别下的并发问题,以及如何选择合适的隔离级别。


所有评论

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