访问内存优化表的事务可能会出现错误情况。
-
- 当前事务尝试更新自事务启动以来已更新的记录。
-
- 由于可重复读取验证失败,当前事务未能提交。
-
- 由于可序列化的验证失败,当前事务未能提交。
-
- 当前事务所依赖的先前事务已中止,导致当前事务无法提交。
并发执行事务时的干扰是这些错误的常见原因。 常见的纠正措施是重新尝试该交易过程。
有关这些错误条件的详细信息,请参阅 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 表的事务隔离级别指南