5

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

表脚本

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

GO

会话 - 1

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
begin tran
select * from dummy where firstname = 'abc'

会议 - 2

insert into dummy values('lmn', 'lmn') -- Why this blocks?
4

2 回答 2

11

您的测试场景中的第一个问题是该表在firstname. 第二个是桌子是空的。

从BOL 中的键范围锁定

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

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

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

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

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

CREATE CLUSTERED INDEX [IX_FirstName] ON [dbo].[dummy] ([firstname] ASC)

……你会发现你还是被屏蔽了!

尽管现在存在一个合适的索引并且执行计划显示它被寻找以满足查询。

您可以通过运行以下命令来了解原因

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 

退货

+---------------+----------------------+--------------+
| resource_type | resource_description | request_mode |
+---------------+----------------------+--------------+
| DATABASE      |                      | S            |
| OBJECT        |                      | IS           |
| PAGE          | 1:198                | IS           |
| KEY           | (ffffffffffff)       | RangeS-S     |
+---------------+----------------------+--------------+

SQL Server 不仅仅对您在查询中指定的范围进行范围锁定。

对于唯一索引上的相等谓词,如果有匹配的键,它将只采用常规锁,而不是任何类型的范围锁。

对于非唯一搜索谓词,它会锁定范围内的所有匹配键以及范围末尾的“下一个”键(ffffffffffff如果不存在“下一个”键,则表示无穷大)。甚至可以在此范围键锁定中使用已删除的“幽灵”记录。

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

如果键不存在,则对唯一和非唯一索引的“下一个”键进行“范围”锁定。如果“next”键不存在,则对“infinity”值进行范围锁定。

因此,对于一个空表,SELECT仍然最终会锁定整个索引。您还需要之前在 and 之间插入一行abclmn然后您的插入才会成功。

insert into dummy values('def', 'def')
于 2010-07-11T23:57:04.560 回答
0

来自http://msdn.microsoft.com/en-us/library/ms173763.aspx

SERIALIZABLE 指定以下内容:

语句不能读取已被其他事务修改但尚未提交的数据。

在当前事务完成之前,任何其他事务都不能修改当前事务已读取的数据。

据我了解,您的插入将被阻止,因为您的 SELECT 正在运行的事务尚未完成。

于 2010-07-09T15:48:49.483 回答