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