我有这张桌子:
TableAB
{
IDA;
IDB;
}
并且我想确保始终有一对(ID1,ID2)和(ID2,ID1)。所以我试图用它来编写脚本:
要插入:
begin tran
insert into tablaAB (IDTablaA, IDTablaB) VALUES(1,2);
insert into tablaAB (IDTablaA, IDTablaB) VALUES(2,1);
commit
要删除:
begin tran
delete tablaAB where IDTablaA = 1 and IDTablaB = 2
delete tablaAB where IDTablaA = 2 and IDTablaB = 1;
commit
我正在使用Microsoft Management Studio的两个实例来运行两个查询,并且在大多数情况下都可以运行,我得到了两行或其中的任何一行。但是有时候,我只能得到其中之一。
步骤是:
在大多数情况下,直到删除两行的事务完成才阻塞,但在某些情况下,它可以传递到下一行以插入第二行。如果发生这种情况,那么我没有一致性数据。
但是我不知道这是因为我在测试中犯了一些错误,还是在极少数情况下,第一个查询没有按我的预期被阻止。
真的,在所有情况下,如果第一次删除完成,第一次插入应该被阻止吗?
表为空。因此,当我尝试删除该行时似乎该行已被阻止,并且不允许插入该行,但是我不知道在某些罕见的情况下该行没有被阻止真的会发生。
谢谢。
答案 0 :(得分:2)
But I don't know if it is because I make some mistakes in the test or in same rare cases the first query is not blocked as I expect.
Really in all cases the first insert should be block if the first delete is done?
似乎您正在使用READ COMMITTED隔离级别运行。在这种情况下,当没有行符合条件时,DELETE会话将不持有任何锁,因此INSERT会话可以继续插入行。这将成为竞态条件,最终您可能会得到零,一或两行。考虑一下导致一行的该序列:
--session 1:
begin tran;
delete TableAB where IDTablaA = 1 and IDTablaB = 2;
--no row deleted, no lock held
--session 2:
begin tran
insert into TableAB (IDTablaA, IDTablaB) VALUES(1,2);
--row inserted, lock held
insert into TableAB (IDTablaA, IDTablaB) VALUES(2,1);
--row inserted, lock held
commit;
-- inserts committed and locks released
--session 1:
delete TableAB where IDTablaA = 2 and IDTablaB = 1;
--row deleted, lock held
commit;
--deleted committed, lock released
如果改为使用SERIALIZABLE
隔离级别,则DELETE
语句将持有一个锁(在这种情况下,由于没有索引,因此是表锁)并阻止插入会话。较少限制的键范围锁将保留,该列上的索引用于定位要删除的行。
请注意,SERIALIZABLE
比没有那么严格的隔离级别更容易产生死锁。