mysql执行SQL时如何进行错误处理与回滚

来源:这里教程网 时间:2026-02-28 20:44:10 作者:

MySQL存储过程中用DECLARE HANDLER捕获错误

在存储过程里执行多条SQL时,单条出错默认不会中断后续语句,更不会自动回滚。必须显式声明错误处理器,否则

INSERT INTO t1 VALUES(1)
失败后,
UPDATE t2 SET x=1
仍会执行。

常见做法是用

DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
捕获所有 SQL 错误,并设置标志位或直接调用
ROLLBACK

DELIMITER $$
CREATE PROCEDURE safe_transfer()
BEGIN
  DECLARE exit_flag INT DEFAULT 0;
  DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
    SET exit_flag = 1;
<p>START TRANSACTION;
INSERT INTO accounts VALUES (1, 100);
UPDATE accounts SET balance = balance - 50 WHERE id = 1;
IF exit_flag = 1 THEN
ROLLBACK;
ELSE
COMMIT;
END IF;
END$$
DELIMITER ;

注意:

CONTINUE
表示错误后继续执行 handler 后面的语句;若用
EXIT
,则 handler 执行完就退出当前 BEGIN...END 块。

客户端代码中判断
mysql_errno()
或异常类型再回滚

PHP、Python 等语言调用 MySQL 时,事务控制权在应用层。不能依赖 MySQL 自动回滚,必须检查每步执行结果。

例如 Python 的

pymysql
中:

执行
cursor.execute()
后不抛异常 ≠ 成功,需检查
cursor.rowcount
或捕获
pymysql.err.IntegrityError
connection.rollback()
必须显式调用,且只能对未提交的事务生效
连接断开、超时、锁等待超时(
Lock wait timeout exceeded
)都属于需回滚的场景

典型错误是只 catch

Exception
却忽略
Warning
—— 比如
INSERT IGNORE
遇到重复键只发 warning,但业务上可能需要感知。

避免隐式提交导致
ROLLBACK
失效

以下操作会**强制提交当前事务**,导致前面的

START TRANSACTION
失去意义,后续
ROLLBACK
无效:

执行 DDL 语句:如
CREATE TABLE
ALTER TABLE
DROP INDEX
执行
LOCK TABLES
/
UNLOCK TABLES
执行
SET AUTOCOMMIT = 1
(哪怕只是临时切换)
执行
SELECT ... FOR UPDATE
在某些隔离级别下也可能触发

调试时可查

SHOW ENGINE INNODB STATUS
中的
TRANSACTIONS
部分,确认事务是否已被隐式提交。

事务中慎用非事务型表(如 MyISAM)

如果事务里混用了 MyISAM 表,

ROLLBACK
对它完全无效——MyISAM 不支持事务,任何写入都会立即持久化。

排查方式:

执行
SHOW CREATE TABLE table_name
查看
ENGINE=
类型
统一使用
ENGINE=InnoDB
,并在建表时显式指定,避免依赖默认引擎配置
跨库操作时注意目标库的默认存储引擎,
CREATE TABLE t AS SELECT ...
可能继承源表引擎,也可能用默认引擎

最易被忽略的是开发环境用 InnoDB、测试环境因配置差异用了 MyISAM,导致回滚逻辑在测试阶段“看似有效”,上线后失效。

相关推荐