如何实现可序列化的事务?

时间:2013-12-09 15:08:34

标签: sql sql-server database relational-database isolation-level

我没有看到如何在不锁定整个表的情况下实现可序列化隔离级别 - 这似乎是可能的,因为我可以找到有关行和范围锁的主题的所有材料。

我在讨论serializable和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 wiki也讨论了这个例子here。]

1 个答案:

答案 0 :(得分:2)

dbcc traceon(3604,1200,-1)

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRAN

-- returns 1
SELECT id FROM marbles m WHERE m.color = 'White';

COMMIT

dbcc traceoff(3604,1200,-1)

提供输出

Process 55 acquiring IS lock on OBJECT: 1:2072783258:0  (class bit2000000 ref1) result: OK

Process 55 acquiring IS lock on PAGE: 1:1:23838  (class bit2000000 ref0) result: OK

Process 55 acquiring RangeS-S lock on KEY: 1:72057594087014400 (8194443284a0) (class bit2000000 ref0) result: OK

Process 55 acquiring RangeS-S lock on KEY: 1:72057594087014400 (61a06abd401c) (class bit2000000 ref0) result: OK

Process 55 acquiring RangeS-S lock on KEY: 1:72057594087014400 (ffffffffffff) (class bit2000000 ref0) result: OK

该计划显示了聚集索引扫描。表中有两行,但取出了三个键范围锁。

WHERE子句没有合适的索引可以在color上搜索,因此select查询有效地最终会锁定整个表。显示的最后一个锁定阻止范围达到“无穷大”(ffffffffffff)。

即使在“颜色”上有合适的索引并且执行计划使用它,锁定的确切范围取决于表中存在的其他行(包括尚未清理的任何已删除的“重影”记录)

A more detailed explanation is available here