键范围锁定

在使用可序列化事务隔离级别时,对于 Transact-SQL 语句读取的记录集,键范围锁可以隐式保护该记录集中包含的行范围。可序列化隔离级别要求每当在事务期间执行任一查询时,该查询都必须获取相同的行集。键范围锁可防止其他事务插入其键值位于可序列化事务读取的键值范围内的新行,从而确保满足此要求。

键范围锁可防止幻读。通过保护行之间的键范围,它还可以防止对事务访问的记录集进行幻插入。

键范围锁放置在索引上,指定开始键值和结束键值。此锁将阻止任何要插入、更新或删除任何带有该范围内的键值的行的尝试,因为这些操作会首先获取索引上的锁。例如,可序列化事务可能发出了一个 SELECT 语句,以读取其键值介于 'AAA''CZZ' 之间的所有行。从 'AAA''CZZ' 范围内的键值上的键范围锁可阻止其他事务插入带有该范围内的键值(例如 'ADG''BBD''CAL')的行。

键范围锁模式

键范围锁包括按范围-行格式指定的范围组件和行组件:

  • 范围表示保护两个连续索引项之间的范围的锁模式。

  • 行表示保护索引项的锁模式。

  • 模式表示使用的组合锁模式。键范围锁模式由两部分组成。第一部分表示用于锁定索引范围 (RangeT) 的锁类型,第二部分表示用于锁定特定键 (K) 的锁类型。这两部分用连字符 (-) 连接,例如 RangeT-K

    范围

    模式

    说明

    RangeS

    S

    RangeS-S

    共享范围,共享资源锁;可序列化范围扫描。

    RangeS

    U

    RangeS-U

    共享范围,更新资源锁;可序列化更新扫描。

    RangeI

    Null

    RangeI-N

    插入范围,空资源锁;用于在索引中插入新键之前测试范围。

    RangeX

    X

    RangeX-X

    排他范围,排他资源锁;用于更新范围中的键。

注意注意

内部空锁模式与所有其他锁模式兼容。

键范围锁模式有一个兼容性矩阵,表示哪些锁与在重叠键和范围上获取的其他锁兼容。有关完整的锁兼容性矩阵,请参阅锁兼容性

 

现有的授权模式

 

 

 

 

 

 

请求的模式

S

U

X

RangeS-S

RangeS-U

RangeI-N

RangeX-X

共享 (S)

更新 (U)

排他 (X)

RangeS-S

RangeS-U

RangeI-N

RangeX-X

转换锁

当键范围锁与其他锁重叠时,将创建转换锁。

锁 1

锁 2

转换锁

S

RangeI-N

RangeI-S

U

RangeI-N

RangeI-U

X

RangeI-N

RangeI-X

RangeI-N

RangeS-S

RangeX-S

RangeI-N

RangeS-U

RangeX-U

在不同的复杂环境下(有时是在运行并发进程时),可以在一小段时间内观察到转换锁。

可序列化范围扫描、单独提取、删除和插入

键范围锁定确保以下操作是可序列化的:

  • 范围扫描查询

  • 对不存在的行的单独提取

  • 删除操作

  • 插入操作

必须满足下列条件才能发生键范围锁定:

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

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

示例

以下表和索引用作随后的键范围锁定示例的基础。

具有索引 B 树图的数据库表

范围扫描查询

为了确保范围扫描查询是可序列化的,每次在同一事务中执行的相同查询应返回同样的结果。其他事务不能在范围扫描查询中插入新行;否则这些插入将成为幻插入。例如,以下查询将使用上图中的表和索引:

SELECT name
    FROM mytable
    WHERE name BETWEEN 'A' AND 'C';

键范围锁放置在与数据行范围(名称在值 Adam 与 Dale 之间的行)对应的索引项上,以防止添加或删除满足上述查询条件的新行。尽管此范围中的第一个名称是 Adam,但是此索引项上的 RangeS-S 模式键范围锁确保了以字母 A 开头的新名称(例如 Abigail)不能添加在 Adam 之前。同样,Dale 索引项上的 RangeS-S 键范围锁确保了以字母 C 开头的新名称(例如 Clive)不能添加在 Carlos 之后。

注意注意

包含的 RangeS-S 锁数量为 n+1,此处 n 是满足查询条件的行数。

对不存在的数据的单独提取

如果事务中的查询试图选择不存在的行,则以后在相同的事务中发出这一查询时,必须返回相同的结果。不允许其他事务插入不存在的行。例如,对于下面的查询:

SELECT name
    FROM mytable
    WHERE name = 'Bill';

键范围锁放置在与从 Ben 到 Bing 的名称范围对应的索引项上,因为名称 Bill 将插入到这两个相邻的索引项之间。RangeS-S 模式键范围锁放置在索引项 Bing 上。这样可阻止其他任何事务在索引项 Ben 与 Bing 之间插入值(例如 Bill)。

删除操作

在事务中删除值时,在事务执行删除操作期间不必锁定该值所属的范围。锁定删除的键值直至事务结束足以保持可序列化性。例如,对于下面的 DELETE 语句:

DELETE mytable
    WHERE name = 'Bob';

排他锁(X 锁)放置在与名称 Bob 对应的索引项上。其他事务可以在删除的值 Bob 的前后插入或删除值。但是任何试图读取、插入或删除值 Bob 的事务都将被阻塞,直到删除的事务提交或回滚为止。

可以使用三个基本锁模式执行范围删除:行锁、页锁或表锁。行、页或表锁定策略由查询优化器确定,或者可以由用户通过优化程序提示(例如 ROWLOCK、PAGLOCK 或 TABLOCK)来指定。当使用 PAGLOCK 或 TABLOCK 时,如果从某个索引页中删除所有的行,则数据库引擎将立即释放该索引页。相反,当使用 ROWLOCK 时,所有删除的行只是标记为已删除;以后通过后台任务从索引页中删除它们。

插入操作

在事务中插入值时,在事务执行插入操作期间不必锁定该值所属的范围。锁定插入的键值直至事务结束足以维护可序列化性。例如,对于下面的 INSERT 语句:

INSERT mytable VALUES ('Dan');

RangeI-N 模式键范围锁放置在与名称 David 对应的索引项上,以测试范围。如果已授权锁,则插入 Dan,并且排他锁(X 锁)将放置在值 Dan 上。RangeI-N 模式键范围锁仅对测试范围是必需的,而不在执行插入操作的事务期间保留。其他事务可以在插入的值 Dan 的前后插入或删除值。但是,任何试图读取、插入或删除值 Dan 的事务都将被阻塞,直到插入的事务提交或回滚为止。