MySQL存储过程不支持try-catch,仅可用DECLARE HANDLER;EXIT HANDLER可退出当前块但不自动回滚,必须显式ROLLBACK,且无法获取错误详情,事务状态需手动管理。

MySQL存储过程里没有try-catch,只有DECLARE HANDLER
MySQL服务端压根不支持try、catch关键字,所有在SQL脚本或存储过程中写try { START TRANSACTION; }这种语法,必然报错:ERROR 1064 (42000): You have an error in your SQL syntax。所谓“类似try-catch”,实际只靠DECLARE HANDLER这一种机制,且它既不能中断当前执行流,也不能跳转到任意位置——它只是在语句出错后设置一个变量,或执行一条简单语句(比如SET @err = 1),后续逻辑仍需你手动判断和分支。
DECLARE EXIT HANDLER FOR SQLEXCEPTION会立即退出当前BEGIN...END块
这是最接近“catch”行为的用法,但要注意:它不会回滚事务,也不会自动COMMIT或ROLLBACK,更不会捕获错误详情(如错误号、消息)。它只做一件事:当块内任意语句触发SQLEXCEPTION时,立刻跳出当前BEGIN...END,继续执行该块之后的语句。
-
DECLARE CONTINUE HANDLER:出错后继续执行下一条语句(适合记录日志+兜底) -
DECLARE EXIT HANDLER:出错后直接退出整个BEGIN...END块(适合快速失败场景) - 两者都**无法获取错误编号或消息**,
GET DIAGNOSTICS要配合CONTINUE HANDLER才能读取,且仅限MySQL 8.0+ - 事务状态完全不受
HANDLER影响:如果已在事务中,出错后仍处于活跃状态,必须显式ROLLBACK或COMMIT
正确配对START TRANSACTION和ROLLBACK的实操要点
绝大多数线上事故,不是因为没写HANDLER,而是忘了在HANDLER里调用ROLLBACK,或者把ROLLBACK写在了COMMIT之后导致逻辑错乱。关键不是“有没有捕获”,而是“有没有确保回滚被执行”。
- 事务开启必须在
HANDLER声明之后,否则HANDLER无法覆盖事务内语句 -
EXIT HANDLER里必须显式写ROLLBACK,不能依赖“自动回滚”——MySQL没有这回事 - 不要在
HANDLER里调用COMMIT:成功路径应统一由主流程控制,避免分支提交导致部分更新被固化 - 示例结构应为:
BEGIN DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN ROLLBACK; RESIGNAL; -- 可选:重新抛出原错误供上层感知 END; <p>START TRANSACTION; INSERT INTO t1 VALUES (1); UPDATE t2 SET x = 2 WHERE id = 1; -- 若此处失败,直接跳到HANDLER执行ROLLBACK COMMIT; END
为什么别在存储过程中强套应用层思维
很多开发者试图用存储过程模拟Java/Python的try-catch-finally,结果写出一堆嵌套BEGIN...END和重复ROLLBACK逻辑。这反而增加出错概率——MySQL存储过程调试困难、错误信息简陋、事务生命周期难追踪。真正该做的,是把复杂异常分支、重试逻辑、跨库协调这些事交给应用层处理;存储过程只做确定性高、原子性强、无外部依赖的单点操作(比如扣库存+写流水),并保证它自身要么全成、要么全退。
最容易被忽略的一点:DECLARE HANDLER对隐式提交类语句无效,比如CREATE TABLE、ALTER TABLE、DROP DATABASE等DDL语句一旦执行,会强制提交当前事务,此时HANDLER再怎么设也救不回已提交的数据。这类操作根本不该放进需要事务保护的业务流程里。


















