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

发布于 2024-09-09 03:11:08 字数 570 浏览 6 评论 0原文

可串行化事务隔离级别通过阻止事务中对表的任何插入(这些插入与其他事务中的任何 select 语句相冲突)来避免幻读问题。我试图用一个例子来理解它,但即使 select 语句中的过滤器不冲突,它也会阻止插入。我希望能解释一下它为什么会这样。

表脚本

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?

Serializable transaction isolation levels avoids the problem of phantom reads by blocking any inserts to a table in a transaction which are conflicting with any select statements in other transactions. I am trying to understand it with an example, but it blocks insert even if when the filter in the select statement is not conflicting. I would appreciate any explanation on why it behaves in that way.

Table Script

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

GO

Session - 1

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

Session - 2

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

北笙凉宸 2024-09-16 03:11:08

测试场景中的第一个问题是该表在 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' 键适用于唯一索引和非唯一索引。如果按“下一个”键
不存在,则对“无穷大”值进行范围锁定。

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

insert into dummy values('def', 'def')

The first issue in your test scenario is that the table has no useful index on firstname. The second is that the table is empty.

From Key-Range Locking in BOL

Before key-range locking can occur,
the following conditions must be
satisfied:

  • The transaction-isolation level must be set to SERIALIZABLE.
  • The query processor must use an index to implement the range filter
    predicate. For example, the WHERE
    clause in a SELECT statement could
    establish a range condition with this
    predicate: ColumnX BETWEEN N'AAA' AND N'CZZ'. A key-range lock can only be
    acquired if ColumnX is covered by an
    index key.

There is no suitable index to take RangeS-S locks on so to guarantee serializable semantics SQL Server needs to lock the whole table.

If you try adding a clustered index on the table on the first name column as below and repeat the experiment ...

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

... you will find that you are still blocked!

Despite the fact that a suitable index now exists and the execution plan shows that it is seeked into to satisfy the query.

You can see why by running the following

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 

Returns

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

SQL Server does not just take out a range lock on exactly the range you specify in your query.

For an equality predicate on a unique index if there is a matching key it will just take a regular lock rather than any type of range lock at all.

For a non unique seek predicate it takes out locks on all matching keys within the range plus the "next" one at the end of the range (or on ffffffffffff to represent infinity if no "next" key exists). Even deleted "ghost" records can be used in this range key locking.

As described here for an equality predicate on either a unique or non unique index

If the key does not exist, then the ‘range’ lock is taken on the
‘next’ key both for unique and non-unique index. If the ‘next’ key
does not exist, then a range lock is taken on the ‘infinity’ value.

So with an empty table the SELECT still ends up locking the entire index. You would need to also have previously inserted a row between abc and lmn and then your insert would succeed.

insert into dummy values('def', 'def')
萌面超妹 2024-09-16 03:11:08

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

可序列化
指定以下内容:

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

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

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

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

SERIALIZABLE
Specifies the following:

Statements cannot read data that has been modified but not yet committed by other transactions.

No other transactions can modify data that has been read by the current transaction until the current transaction completes.

As I understand this, your insert will be blocked since the transaction under which your SELECT is running has not completed.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文