我没有看到如何在不锁定整个表格的情况下实现可序列化的隔离级别 - 这似乎是可能的,因为我可以找到有关该主题的所有材料,并讨论行和范围锁定。如何实现可序列化事务?
我在讨论可序列化和Sql Server的快照隔离级别here时发现了一个有趣的例子。
这个例子是关于有两个弹珠,一个黑色,一个白色和两个交易,它们同时将所有黑色大理石变成白色,反之亦然。我的问题是:可序列化的隔离级别如何防止最终结果再次是一个白色和一个黑色大理石的结果?
我试过SQL Server上的以下内容:
create table marbles (id int primary key, color char(5))
insert marbles values(1, 'White')
insert marbles values(2, 'Black')
我现在启动两个交易,一个是
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRAN
-- returns 1
SELECT id FROM marbles m WHERE m.color = 'White';
UPDATE marbles SET color = 'Black' WHERE id = 1;
COMMIT
另一个是类似的:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRAN
-- returns 2
SELECT id FROM marbles m WHERE m.color = 'Black';
UPDATE marbles SET color = 'White' WHERE id = 2;
COMMIT
的选择意味着类似于程序客户端的读取,该程序客户端使用该id稍后发出相应的更新。
如果我先运行两个事务,直到select语句先执行,然后让它们继续执行,则它们会死锁。这是我们希望发生的事情(或者至少比混合颜色的结果更好)。
我试图将更新设置为分别将颜色设置为绿色和黄色,并且事务仍处于死锁状态,这次他们确实不应该这样做。如果白色大理石变成黄色,那么对于黑色大理石感兴趣的交易就不会选择它 - 为什么会陷入僵局?
为了正确地实现序列化(正好锁定必要的情况),必须保存一个事务选择的记录,以便数据库引擎可以计算其他事务的更新如何影响这些结果集。
我可以看到,在这个例子中的简单情况下,或者简单的范围查询,但是在一般情况下,它会让我觉得这是一个非常复杂的问题。
我觉得在这方面令人困惑的是,我没有找到关于该主题的消息来源提到这个问题。他们都只是说了“可序列化的隔离级别是通过聪明的锁定实现的,参见范围锁定”,并表明这是事实。但那不是全部,是吗?
特别是,当数据库引擎声称“支持可序列化”时,它根本不清楚可能是什么:它可能是每个事务上的所有事物的简单锁定,它可能永远不会成为锁定的另一个极端真正有必要的是,这太复杂了,无法实施。
相比之下,很明显Sql Server的快照隔离级别究竟意味着什么。
如果我看得很清楚,我会很感兴趣,如果有的话,人们是否有指向进一步读取主题的指针,特别是关于SQL Server。
[编辑:在Postgres的世界类似的问题已经被问here]
[EDIT2:Postgres的维基也有这个非常例子here讨论]
链接的文章是我已经看过的解释范围查询的范围锁之一。这解释了在特定情况下如何完成这项工作,但似乎一般情况似乎更为复杂。毕竟,我的例子甚至没有范围查询,它也可能同时具有两个“之间”子句 - 它可以任意复杂。 – John
@John - 不那么复杂。除非计划具有索引查找,否则可序列化将始终锁定整个事物(无论是通过对象级锁还是包含整个索引的单个范围锁)。然后个别范围可能被锁定。 –
我试图添加一个索引,并在两种情况下再次出现死锁。 – John