Memory-Optimized 表上的事务重试逻辑指南

访问内存优化表的事务可能会出现错误情况。

    1. 当前事务尝试更新自事务启动以来已更新的记录。
    1. 由于可重复读取验证失败,当前事务未能提交。
    1. 由于可序列化的验证失败,当前事务未能提交。
    1. 当前事务所依赖的先前事务已中止,导致当前事务无法提交。

并发执行事务时的干扰是这些错误的常见原因。 常见的纠正措施是重新尝试该交易过程。

有关这些错误条件的详细信息,请参阅 Memory-Optimized 表中事务的冲突检测、验证和提交依赖项检查部分。

内存优化表无法发生死锁(错误代码 1205)。 内存优化表不使用锁。 但是,如果应用程序已包含死锁的重试逻辑,可以扩展现有逻辑以包含新的错误代码。

重试注意事项

应用程序通常会在事务之间遇到冲突,并且需要实现重试逻辑来解决这些冲突。 遇到的冲突数取决于多种因素:

  • 单行的竞争。 随着尝试更新同一行的事务数的增加,冲突的可能性会增加。

  • REPEATABLE READ 事务读取的行数。 读取的行越多,这些行被并发事务更新的可能性就越大。 这会导致可重复读取验证失败。

  • SERIALIZABLE 事务使用的扫描范围的大小。 扫描范围越大,并发事务引入虚拟行的可能性就越大,导致可序列化验证失败。

    应用程序很难避免这些冲突,需要重试逻辑。

重要

访问内存优化表的读写事务需要重试逻辑。

关于 Read-Only 事务和原生编译存储过程的注意事项

单次执行本机编译存储过程的只读事务不需要对 REPEATABLE READ 和 SERIALIZABLE 事务进行验证。 由于事务是只读的,写入冲突无法发生。

但是,依赖故障仍可能发生。 依赖项失败比因冲突导致的错误少见。 因此,在许多情况下,对于跨本机编译存储过程的单个执行的只读事务,不需要特定的重试逻辑。

有关 Read-Only 事务和跨容器事务的注意事项

只读跨容器事务(即在本机编译存储过程的上下文之外启动的事务)在 SNAPSHOT 隔离下访问内存优化表时不执行验证。 但是,在 REPEATABLE READ 或 SERIALIZABLE 隔离下访问内存优化表时,将在提交时执行验证。 在这种情况下,可能需要重试逻辑。

有关详细信息,请参阅有关 事务隔离级别中跨容器事务的部分。

实施重试机制

与访问内存优化表的所有事务一样,需要考虑重试逻辑来处理潜在的故障,例如写入冲突(错误代码 41302)或依赖项故障(错误代码 41301)。 在大多数应用程序中,失败率会较低,但仍需要通过重试事务来处理失败。 实现重试逻辑的两种建议方法是:

  • 客户端重试。 客户端重试是一般情况下实现重试逻辑的首选方法。 客户端应用程序捕获事务引发的错误,并重试事务。 如果现有客户端应用程序具有用于处理死锁的重试逻辑,则可以扩展应用程序以处理新的错误代码。

  • 使用封装存储过程。 客户端调用解释的 Transact-SQL 存储过程,该存储过程调用本机编译的存储过程或执行事务。 包装过程随后使用 try/catch 逻辑捕获错误,并根据需要重试过程调用。 失败发生之前,结果可能已经返回给客户端,而客户端不知道需要丢弃这些结果。 因此,为了安全起因,最好只将此方法与本机编译的存储过程一起使用,这些存储过程不会将任何结果集返回给客户端。

可以在 Transact-SQL 或中间层的应用程序代码中实现重试逻辑。

考虑重试逻辑的两个可能原因是:

  • 客户端应用程序针对其他错误代码(例如 1205)具有重试逻辑,你可以扩展该逻辑。

  • 冲突很少见,使用准备好的执行来减少端到端延迟非常重要。 有关直接执行本机编译存储过程的详细信息,请参阅 本机编译的存储过程

以下示例演示了解释型 Transact-SQL 存储过程中的重试逻辑,该过程包含对本地编译的存储例程或涉及跨容器事务的调用。

CREATE PROCEDURE usp_my_procedure @param1 type1, @param2 type2, ...  
AS  
BEGIN  
  -- number of retries - tune based on the workload  
  DECLARE @retry INT = 10  
  
  WHILE (@retry > 0)  
  BEGIN  
    BEGIN TRY  
  
      -- exec usp_my_native_proc @param1, @param2, ...  
  
      --       or  
  
      -- BEGIN TRANSACTION  
      --   ...  
      -- COMMIT TRANSACTION  
  
      SET @retry = 0  
    END TRY  
    BEGIN CATCH  
      SET @retry -= 1  
  
      -- the error number for deadlocks (1205) does not need to be included for   
      -- transactions that do not access disk-based tables  
      IF (@retry > 0 AND error_number() in (41302, 41305, 41325, 41301, 1205))  
      BEGIN  
        -- these error conditions are transaction dooming - rollback the transaction  
        -- this is not needed if the transaction spans a single native proc execution  
        --   as the native proc will simply rollback when an error is thrown   
        IF XACT_STATE() = -1  
          ROLLBACK TRANSACTION  
  
        -- use a delay if there is a high rate of write conflicts (41302)  
        --   length of delay should depend on the typical duration of conflicting transactions  
        -- WAITFOR DELAY '00:00:00.001'  
      END  
      ELSE  
      BEGIN  
        -- insert custom error handling for other error conditions here  
  
        -- throw if this is not a qualifying error condition  
        ;THROW  
      END  
    END CATCH  
  END  
END  

另请参阅

了解 Memory-Optimized 表上的事务
Memory-Optimized 表里的交易
Memory-Optimized 表的事务隔离级别指南