SQL Server 存储过程中无法用TRY CATCH捕获1205死锁错误,因其由系统直接终止执行上下文;有效捕获点仅在最外层批处理或应用层(如SqlAzureExecutionStrategy)。

SQL Server 存储过程里不能用 TRY CATCH 捕获 1205 死锁错误
死锁发生时,SQL Server 会直接将当前事务标记为牺牲品并强制终止整个执行上下文,TRY CATCH 块根本不会运行。你在存储过程里写再多层 BEGIN TRY ... END CATCH,只要错误号是 1205,控制权就不会跳进去——应用层看到的仍是原始错误: Transaction (Process ID XX) was deadlocked。
- 常见误操作包括:在存储过程开头加
TRY CATCH、在触发器里放重试逻辑、或把WAITFOR DELAY写在CATCH里指望它执行 - 真正有效的捕获点只在最外层批处理:即调用该存储过程的那个 SQL 脚本、SSMS 查询窗口、或由应用拼接发送的完整语句块
- 如果你依赖 ORM(如 Entity Framework),应启用
SqlAzureExecutionStrategy,而不是试图在存储过程中“自己搞”
MySQL 存储过程可用 DECLARE EXIT HANDLER 实现有限重试
MySQL 支持在存储过程中用 DECLARE EXIT HANDLER FOR 1213, 1205 捕获死锁和锁超时,配合 REPEAT ... UNTIL 实现最多 3 次尝试。但要注意限制条件:
- 外部不能已开启事务,否则
START TRANSACTION会报错ERROR 1305 (42000): SAVEPOINT does not exist - 必须显式
ROLLBACK后再DO SLEEP(0.01),避免忙等;不能用WAITFOR(那是 SQL Server 语法) - 示例关键片段:
DECLARE CONTINUE HANDLER FOR 1213, 1205<br> BEGIN<br> SET @retry := @retry + 1;<br> ROLLBACK;<br> IF @retry < 3 THEN<br> DO SLEEP(0.01);<br> ELSE<br> RESIGNAL;<br> END IF;<br> END;
重试必须包裹整个事务,而非单条语句
把 UPDATE 单独包进 TRY CATCH 并重试,等于没做任何事——死锁发生在资源争用层面,不是语句执行失败。真正要重试的是“从 BEGIN TRANSACTION 到 COMMIT”这一整段逻辑。
- 错误写法:
BEGIN TRY; UPDATE ... ; END TRY→ 即使重试,事务状态已乱,@@TRANCOUNT可能不一致 - 正确结构:整个事务块(含
EXEC YourStoredProcedure)必须在TRY内,且COMMIT成功后才BREAK -
WAITFOR DELAY '00:00:00.05'是底线,太短易引发重试风暴;超过 3 次必须THROW原错误,否则掩盖真实瓶颈
Oracle 存储过程靠 SAVEPOINT + 循环重试,但不能嵌套事务
Oracle 没有 TRY CATCH,但可用 SAVEPOINT 配合 EXCEPTION WHEN OTHERS 实现可控回退。关键约束是:PL/SQL 不允许嵌套事务,所以每次循环都得从干净状态开始。
- 必须在循环内建
SAVEPOINT sp_start,出错时ROLLBACK TO sp_start,不能ROLLBACK全局事务 -
DBMS_LOCK.SLEEP(2)推荐用于人工干预场景;高并发下建议改用更小值(如0.1)并配指数退避 - 注意:如果过程里调用了数据库链接(
@dblink),死锁概率陡增,此时重试成功率反而下降,应优先检查远程锁持有时间
重试逻辑最容易被忽略的点,是它从来不是兜底方案——而是暴露设计缺陷的探针。当重试次数频繁触达上限,真正该动的不是延迟时间或最大重试数,而是表更新顺序、索引覆盖、事务粒度这些底层结构。

















