当另一个事务的事务隔离级别可以使用非冲突过滤器进行序列化时,为什么要插入 TSQL 语句块?
可串行化事务隔离级别通过阻止事务中对表的任何插入(这些插入与其他事务中的任何 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
测试场景中的第一个问题是该表在
firstname
上没有有用的索引。第二个是桌子是空的。来自 键范围锁定 BOL
没有合适的索引来获取 RangeS-S 锁,因此为了保证可序列化语义,SQL Server 需要锁定整个表。
如果你尝试如下所示在表的名字列上添加聚集索引并重复实验……
你会发现你仍然被阻塞!
尽管现在存在合适的索引,并且执行计划显示正在寻找它来满足查询。
您可以通过运行以下
命令
来了解为什么SQL Server 不只是对您在查询中指定的范围进行范围锁定。
对于唯一索引上的相等谓词,如果有匹配的键,它将只采用常规锁,而不是任何类型的范围锁。
对于非唯一查找谓词,它会获取范围内所有匹配键的锁以及范围末尾的“下一个”键(如果不存在“下一个”键,则在
ffffffffffff
上表示无穷大) )。 甚至删除的“幽灵”记录在此范围内使用键锁定。如此处所述 对于相等谓词唯一索引或非唯一索引
因此,对于空表,
SELECT
最终仍会锁定整个索引。您还需要事先在abc
和lmn
之间插入一行,然后插入才会成功。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
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 ...
... 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
Returns
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
So with an empty table the
SELECT
still ends up locking the entire index. You would need to also have previously inserted a row betweenabc
andlmn
and then your insert would succeed.来自 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.