MySQL存储过程需用DECLARE CONTINUE HANDLER捕获SQLEXCEPTION并手动回滚,否则单条SQL失败不中断后续执行;应用层调用时也须显式检查错误并调用ROLLBACK,且要避免DDL、SET AUTOCOMMIT=1等隐式提交操作及MyISAM等非事务表。

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;
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,导致回滚逻辑在测试阶段“看似有效”,上线后失效。










