SQL 服务器 - 对可序列化隔离级别感到困惑

SQL Server - confusion about serializable isolation level

我已阅读文章 (https://www.simple-talk.com/sql/t-sql-programming/questions-about-t-sql-transaction-isolation-levels-you-were-too-shy-to-ask/),我有一个问题根据:

"SERIALIZABLE: A query in the current transaction cannot read data modified by another transaction that has not yet committed. No other transaction can modify data being read by the current transaction until it completes, and no other transaction can insert new rows that would match the search condition in the current transaction until it completes. As a result, the Serializable isolation level prevents dirty reads, nonrepeatable reads, and phantom reads. However, it can have the biggest impact on performance, compared to the other isolation levels."

我对插入不满足来自 1 session/query 的搜索条件的新行感到困惑。示例如下:

假设我有 table

EmpID   FirstName
1       john
2       new employee
3       A new employee

并在单独的选项卡中查询:

--session 1----------------------------------
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

BEGIN TRANSACTION;

    SELECT FirstName FROM EmployeeInfo
    WHERE FirstName like 'new empl%'

    WAITFOR DELAY '00:00:10'  

    SELECT FirstName FROM EmployeeInfo
    WHERE FirstName like 'new empl%'

ROLLBACK TRANSACTION;

---------session 2---------------------------
begin transaction;

    UPDATE EmployeeInfo
    SET FirstName = 'frank'
    WHERE EmpID = 1;

commit transaction;

-----session 3----
insert into EmployeeInfo values('A new employe 2')

我一个接一个地执行查询:会话 1、会话 2、会话 3。 我预计会话 1 不会停止会话 2 和会话 3 的执行,因为来自该会话的更新和插入不满足第一个查询中使用的搜索条件。但是,在结果中,我可以看到会话 1 必须在会话 2 和会话 3 执行之前完成(回滚)。

但是,当我在会话 1 中使用其他搜索条件时,如下所示:

--session 1
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

BEGIN TRANSACTION;

SELECT EmpID, FirstName FROM EmployeeInfo
WHERE EmpID = 2

WAITFOR DELAY '00:00:10'  

SELECT FirstName FROM EmployeeInfo

ROLLBACK TRANSACTION;

那么,session 2和session 3独立于session 1完成。 这是为什么?为什么喜欢条件块插入而“=”不喜欢?

已编辑: 1、EmpID上只有一个主键。

这是因为"no other transaction can insert new rows that would match the search condition in the current transaction until it completes"。 SQL 服务器通过获取范围锁来防止插入冲突来强制执行此操作。

如果您在 EmployeeInfo.FirstName 上有索引,SQL 可能会采用窄锁来强制执行此操作。但是没有索引,SQL 需要一个锁来防止任何插入。此外,如果索引不支持 SELECT 查询谓词,它将阻止所有插入。

您可以使用以下方法检查锁的当前状态:

select @@spid this_session, *
from sys.dm_tran_locks

。请注意,此行为使 SERIALIZABLE 成为一个不是很有用的隔离级别。而且您真的应该只使用 READ COMMITTED 和 SNAPSHOT,可能会为特定事务添加锁定提示。