SQL存储过程重试机制实现策略

作者:技术拾光时间:2026-07-16 20:01:15浏览:423

SQLServer中死锁错误(1205)无法在存储过程内被TRYCATCH捕获,重试逻辑必须置于最外层批处理,包裹整个事务并显式判断错误号。MySQL可用DECLAREEXITHANDLER处理,Oracle依赖SAVEPOINT和LOOP循环实现可控重试。

在SQL Server存储过程中处理死锁(错误号1205)时,有个经常被忽略的坑:TRY CATCH根本抓不住它。死锁一旦发生,执行上下文会被直接终止,CATCH块连门都进不去。所以,重试逻辑必须放在最外层批处理,把整个事务(BEGIN TRAN/EXEC/COMMIT)包起来,显式判断ERROR_NUMBER()=1205,配合WAITFOR DELAY延时,还得限制重试次数。下面逐个说清楚。

SQL存储过程重试机制实现策略

SQL Server 存储过程里不能用 TRY CATCH 捕获 1205 死锁错误

死锁一发生,SQL Server 二话不说就把当前执行上下文给掐了,BEGIN TRY 块根本来不及进入 CATCH。你在存储过程里写再多层嵌套,只要错误号是 1205,控制权就不会跳进去。常见的现象包括:应用层还是收到 Transaction (Process ID XX) was deadlocked;CATCH 里的 WAITFOR DELAY 完全没执行;在触发器或嵌套过程中放重试逻辑也跟没放一样。

重试必须由最外层批处理控制,且包裹整个事务

真正能捕获并重试的位置,是调用该存储过程的那个 T-SQL 批处理(比如 SSMS 查询窗口、SQLCMD 脚本、或应用拼接后发送的完整语句块)。关键点不是“在哪写重试”,而是“重试什么”:

  • 必须把 BEGIN TRANSACTION、EXEC YourStoredProcedure、COMMIT TRANSACTION 全部包进 TRY 块内
  • 单独对某条 UPDATE 加 TRY CATCH 并重试,会导致 @@TRANCOUNT 错乱、事务状态污染
  • ERROR_NUMBER() 必须显式判断是否等于 1205,其他错误(如约束冲突、超时)不应重试
  • WAITFOR DELAY '00:00:00.05' 是底线,太短易引发重试风暴;超过 3 次必须 THROW 原错误

MySQL 存储过程可用 DECLARE EXIT HANDLER 实现有限重试

MySQL 支持在存储过程中用 DECLARE EXIT HANDLER FOR 1213, 1205 捕获死锁和锁超时,但有硬性限制:

  • 外部不能已开启事务,否则 START TRANSACTION 会报错 ERROR 1305 (42000): SAVEPOINT does not exist
  • 必须显式 ROLLBACK 后再调用 DO SLEEP(0.01),不能用 WAITFOR(那是 SQL Server 语法)
  • 重试逻辑要包裹整个事务块,而非单条语句;示例中用 @retry := @retry + 1 计数,IF @retry < 3 控制上限

Oracle 存储过程靠 SAVEPOINT + LOOP 实现可控重试

Oracle 没有 TRY CATCH,但可用 SAVEPOINT 配合 EXCEPTION WHEN OTHERS 回退到干净状态:

  • 每次循环开始前设 SAVEPOINT sp_start,失败后 ROLLBACK TO sp_start,避免状态累积
  • 禁止在循环内 COMMIT;所有 COMMIT 必须放在成功退出循环之后
  • 优先捕获具体异常码(如 ORA-03113、ORA-03114),而非只写 WHEN OTHERS
  • DBMS_SCHEDULER.CREATE_JOB 的 max_failures 不是过程内重试——那是任务级调度,每次都是全新会话,和事务重试无关

重试机制最易被忽略的点,是误把“失败后再次执行”当成“事务内重试”。实际上,数据库层面真正的重试,永远依赖于上层对整个事务边界的重新发起,而不是过程体内的循环或触发器补救。