如何防止池连接中事务隔离级别的泄漏?

How to prevent leak of transaction isolation level in pooled connections?

我在 dot net core 2.2 项目中使用 System.Data.SqlClient (4.6.1)。 SqlClient 维护一个连接池,据报道,如果下一个 sql 命令使用相同的池连接,它会泄漏事务隔离级别。

例如,这个 Whosebug 答案对此进行了解释:

我试图寻找防止这种泄漏的正确方法,但找不到令人满意的解决方案。

我想遵循这个模式:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
SET XACT_ABORT ON -- Turns on rollback if T-SQL statement raises a run-time error.

BEGIN TRANSACTION
SELECT * FROM MyTable;
-- removed complex statements for brevity. there are selects followed by insert.
COMMIT TRANSACTION

-- Set settings back to known defaults.
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
SET XACT_ABORT OFF

这是一个好方法吗?

我通常会使用两个不同的独立连接字符串(例如,使用经过调整的 Application Name 值)。将一个连接字符串用于普通连接,另一个用于需要序列化的连接。

由于连接字符串不同,它们进入不同的池。如果您认为这会导致问题,您可能需要调整其他与池相关的设置(例如,如果很少使用可序列化池,则将其限制为低得多的最大值,并防止可能创建 2x 默认最大连接数)。

根据@Zohar Peled 在评论中的建议回答我自己的问题:

BEGIN TRY
    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
    SET XACT_ABORT ON -- Turns on rollback if T-SQL statement raises a run-time error.

    BEGIN TRANSACTION
    SELECT * FROM MyTable;
    -- removed complex statements for brevity. there are selects followed by multiple inserts.
    COMMIT TRANSACTION

    SET TRANSACTION ISOLATION LEVEL READ COMMITTED
    SET XACT_ABORT OFF
END TRY
BEGIN CATCH
    SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
    SET XACT_ABORT OFF;
    THROW;
END CATCH

编辑: 如果您在存储过程中设置 isloation 级别和 xact_abort,则它的范围仅限于存储过程,您不需要捕获并关闭所有内容。 https://docs.microsoft.com/en-us/sql/t-sql/statements/set-statements-transact-sql?view=sql-server-ver15.

我建议永远不要更改事务隔离级别。如果事务需要不同的锁定行为,请对选定的查询使用适当的锁定提示。

事务隔离级别是生硬的工具,通常会产生意想不到的后果。

SERIALIZABLE 特别有问题,因为很少有人准备好处理它用来强制执行隔离保证的死锁。

此外,如果您仅更改存储过程中的事务隔离级别,SQL服务器将在过程完成后自动恢复会话的隔离级别。