由于不同的隔离级别,导致 SQL Server 事务出现错误

3
我创建了下面的存储过程,以便检查事务中的隔离级别行为:
CREATE PROCEDURE ReadCommittedIsolationLevel
AS
BEGIN
    BEGIN TRANSACTION t1
    BEGIN TRY
        EXEC SnapShotIsolationLevel
        COMMIT TRANSACTION
    END TRY
    BEGIN CATCH
        PRINT ERROR_MESSAGE()
        ROLLBACK TRANSACTION t1
    END CATCH
END

CREATE PROCEDURE SnapShotIsolationLevel
AS
BEGIN
    SET TRANSACTION ISOLATION LEVEL SNAPSHOT        
    BEGIN TRANSACTION t2
        BEGIN TRY
            SELECT TOP 20 * 
            FROM Orders 
            ORDER BY 1 DESC

            COMMIT
        END TRY
        BEGIN CATCH
            PRINT ERROR_MESSAGE()
            ROLLBACK TRAN t2
        END CATCH
END

然后我运行了这个:

EXEC ReadCommittedIsolationLevel

我收到了这个错误信息:
在数据库'MyDataBase'中的事务失败,因为该语句在快照隔离下运行,但事务未在快照隔离下启动。 除非事务最初是在快照隔离级别下启动,否则无法在事务启动后将事务的隔离级别更改为快照。 在没有找到名称为t2的事务或保存点的情况下,无法回滚t2。
如果我删除事务并像普通存储过程一样运行,它就可以正常工作。
为什么会这样呢?
1个回答

6
错误提示已经给出了正确的解释:

在事务开始后,您不能将事务的隔离级别更改为快照。

SQL Server没有“嵌套事务”,您唯一拥有的真正事务是在“读提交”下启动的外部事务,接下来的“begin tran”除了增加“@@trancount”之外什么也不做。您可以在此处阅读更多信息:Paul Randal的“每日一个SQL Server DBA神话”:(26/30)嵌套事务是真实存在的

快照隔离表示在事务级别上具有一致的数据,而不是在语句级别上(RCSI),因此您应该在事务开始时将隔离级别更改为快照,否则您将无法将其更改为快照

当它没有被包含在事务中时,您可以随时将TIL更改为Snapshot,因为根本没有打开的事务,因此您不会发现自己处于“事务已启动并有人尝试将TIL更改为Snapshot”的情况。 - sepupic
这里唯一明显的事情是你不想理解 事务级别一致性 的含义。读提交允许出现不可重复读,所以想象一下你在读提交模式下打开了一个 tran,从 tab 中进行了 count() 操作,得到了 1000 行数据,然后将 TIL 更改为快照模式并再次对 tab 进行 count() 操作。假设这是被允许的。但是在你的第一次和第二次 count(*) 操作之间,tab 中新增了 100 行数据。现在你的事务中存在不可重复读,你的读取结果分别是 1000 和 1100,这就没有事务级别的一致性了。 - sepupic
事务级一致性意味着在一个事务期间内,您可以从同一张表中多次读取,而数据始终保持一致。因此,如果您首先将TIL设置为SNAPSHOT,然后开始您的事务,第一次读取会给您1000行数据,第二次读取也会给您1000行数据,因为您将在BEGIN TRAN时刻接收到数据,并且该时刻仅在操作SNAPSHOT TIL时被记忆。在您的情况下,当您在改变为SNAPSHOT之前启动了事务,没有记忆BEGIN TRAN的时刻。 - sepupic

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接