为什么在另一个事务的事务隔离级别可以使用非冲突过滤器进行序列化时插入TSQL语句块?

Ana*_*tel 5 sql-server isolation-level

可序列化的事务隔离级别通过阻止对事务中与任何其他事务中的任何select语句冲突的表的任何插入来避免幻像读取的问题.我试图用一个例子来理解它,但即使select语句中的过滤器没有冲突,它也会阻塞插入.我会很感激为什么它以这种方式行事的任何解释.

表脚本

CREATE TABLE [dbo].[dummy](
    [firstname] [char](20) NULL,
    [lastname] [char](20) NULL
) ON [PRIMARY]

GO
Run Code Online (Sandbox Code Playgroud)

会议 - 1

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
begin tran
select * from dummy where firstname = 'abc'
Run Code Online (Sandbox Code Playgroud)

会议 - 2

insert into dummy values('lmn', 'lmn') -- Why this blocks?
Run Code Online (Sandbox Code Playgroud)

Mar*_*ith 10

测试场景中的第一个问题是该表没有有用的索引firstname.第二个是表是空的.

从BOL中的键范围锁定

在进行键范围锁定之前,必须满足以下条件:

  • 事务隔离级别必须设置为SERIALIZABLE.

  • 查询处理器必须使用索引来实现范围过滤谓词.例如,WHERE 语句中的子句SELECT可以使用此谓词建立范围条件:ColumnX BETWEEN N'AAA' AND N'CZZ'.只有在IndexX被索引键覆盖时才能获取键范围锁.

没有合适的索引来RangeS-S锁定,以保证SQL Server需要锁定整个表的可序列化语义.

如果您尝试在第一个名称列的表上添加聚集索引,如下所示,并重复实验...

CREATE CLUSTERED INDEX [IX_FirstName] ON [dbo].[dummy] ([firstname] ASC)
Run Code Online (Sandbox Code Playgroud)

......你会发现你仍被封锁!

尽管现在存在合适的索引并且执行计划显示它被寻求满足查询.

您可以通过运行以下内容来了解​​原因

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE

BEGIN TRAN

SELECT *
FROM   dummy
WHERE  firstname = 'abc'

SELECT resource_type,
       resource_description, 
       request_mode
FROM   sys.dm_tran_locks
WHERE  request_session_id = @@SPID

COMMIT 
Run Code Online (Sandbox Code Playgroud)

返回

+---------------+----------------------+--------------+
| resource_type | resource_description | request_mode |
+---------------+----------------------+--------------+
| DATABASE      |                      | S            |
| OBJECT        |                      | IS           |
| PAGE          | 1:198                | IS           |
| KEY           | (ffffffffffff)       | RangeS-S     |
+---------------+----------------------+--------------+
Run Code Online (Sandbox Code Playgroud)

SQL Server不仅仅在您在查询中指定的范围内取出范围锁定.

对于唯一索引上的等式谓词,如果存在匹配键,则它将仅采用常规锁定而不是任何类型的范围锁定.

对于非唯一的搜索谓词,它取出范围内所有匹配键的锁定加上范围末尾的ffffffffffff"下一个"键(如果不存在"下一个"键则表示无穷大).即使删除了"ghost"记录也可以在此范围内使用键锁定.

如此处所述,对于唯一或非唯一索引的等式谓词

如果该密钥不存在,则对于唯一索引和非唯一索引,将在"下一个"密钥上进行"范围"锁定.如果"下一个"键不存在,则对"无穷大"值进行范围锁定.

因此,对于一个空表,SELECT仍然会锁定整个索引.你还需要有先前插入之间的行abclmn,然后您的插入会成功.

insert into dummy values('def', 'def')
Run Code Online (Sandbox Code Playgroud)