如何防止池连接中事务隔离级别的泄漏?
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服务器将在过程完成后自动恢复会话的隔离级别。
我在 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服务器将在过程完成后自动恢复会话的隔离级别。