读取提交的快照 VS 快照隔离级别

Posted

技术标签:

【中文标题】读取提交的快照 VS 快照隔离级别【英文标题】:Read committed Snapshot VS Snapshot Isolation Level 【发布时间】:2011-02-14 00:33:09 【问题描述】:

请有人帮助我了解何时在 SQL Server 中使用 SNAPSHOT 隔离级别而不是 READ COMMITTED SNAPSHOT?

我知道在大多数情况下 READ COMMITTED SNAPSHOT 有效,但不确定何时进行 SNAPSHOT 隔离。

谢谢

【问题讨论】:

【参考方案1】:

READ COMMITTED SNAPSHOT 进行乐观读取和悲观写入。相比之下,SNAPSHOT 进行乐观读取和乐观写入。

对于大多数需要行版本控制的应用,Microsoft 建议使用 READ COMMITTED SNAPSHOT

阅读这篇出色的 Microsoft 文章:Choosing Row Versioning-based Isolation Levels。它解释了两种隔离级别的好处和成本。

这里有一个更彻底的: http://msdn.microsoft.com/en-us/library/ms345124(SQL.90).aspx

【讨论】:

这似乎不正确。见dba.stackexchange.com/a/54681/52708。 乐观读和乐观写有什么区别?谷歌搜索不解释,谢谢, 这是一个术语还不明白的答案,甚至没有人知道乐观读、乐观写、悲观读、悲观写的含义, 乐观和悲观是相当常见的术语,它们确实有意义。我看不出发布的链接与这个答案有何矛盾。【参考方案2】:

[![隔离级别表][2]][2]

请看下面的例子:

读取提交的快照

如下更改数据库属性

ALTER DATABASE SQLAuthority
SET READ_COMMITTED_SNAPSHOT ON WITH ROLLBACK IMMEDIATE
GO

会话 1

USE SQLAuthority
GO
BEGIN TRAN
UPDATE DemoTable
SET i = 4
WHERE i = 1

第 2 场

USE SQLAuthority
GO
BEGIN TRAN
SELECT *
FROM   DemoTable
WHERE i = 1

结果 – 会话 2 中的查询显示旧值 (1, ONE),因为当前事务未提交。这也是避免阻塞和读取已提交数据的方法。

会话 1

COMMIT

第 2 场

USE SQLAuthority
GO
SELECT *
FROM   DemoTable
WHERE i = 1

结果 - 会话 2 中的查询不显示任何行,因为行在会话 1 中更新。因此,我们再次看到已提交的数据。

快照隔离级别

这是新的隔离级别,从 SQL Server 2005 开始提供。对于此功能,应用程序需要进行更改,因为它必须使用新的隔离级别。

使用以下更改数据库设置。我们需要确保数据库中没有事务。

ALTER DATABASE SQLAuthority SET AllOW_SNAPSHOT_ISOLATION ON

现在,我们还需要使用下面的方法来改变连接的隔离级别

会话 1

USE SQLAuthority
GO
BEGIN TRAN
UPDATE DemoTable
SET i = 10
WHERE i = 2

第 2 场

SET TRANSACTION ISOLATION LEVEL SNAPSHOT
GO
USE SQLAuthority
GO
BEGIN TRAN
SELECT *
FROM   DemoTable
WHERE i = 2

结果 - 即使我们将值更改为 10,我们仍然会在会话 2 (2, TWO) 中看到旧记录。

现在,让我们在会话 1 中提交事务

会话 1

COMMIT

让我们回到会话 2 并再次运行 select。

第 2 场

SELECT *
FROM   DemoTable
WHERE i = 2

我们仍然会看到记录,因为会话 2 已经声明了具有快照隔离的事务。除非我们完成交易,否则我们不会看到最新记录。

第 2 场

COMMIT
SELECT *
FROM   DemoTable
WHERE i = 2

现在,我们应该看不到该行,因为它已经更新了。

见:SQL Authority、Safari Books Online

【讨论】:

这个答案的例子比选择的答案好得多。 同意,这是指向外部资源的最佳答案。【参考方案3】:

如果不讨论快照中可能发生的可怕的“快照更新冲突”异常,快照和已提交快照读取的比较是不完整的,但快照已提交读取除外。

简而言之,快照隔离在事务开始时检索已提交数据的快照,然后对读取和写入使用乐观锁定。如果在尝试提交事务时,发现其他东西更改了相同的数据,则数据库将回滚整个事务并引发错误,从而导致调用代码中的快照更新冲突异常。这是因为受事务影响的数据版本在事务结束时与开始时不同。

Snapshot Read Committed 不会遇到这个问题,因为它使用写入锁定(悲观写入)并且它在每个语句的状态 处获取所有已提交数据的快照版本信息。

Snapshot 和 NOT Snapshot Read Committed 发生快照更新冲突的可能性是两者之间极其显着的区别。

【讨论】:

【参考方案4】:

仍然相关,从 Bill 的 cmets 开始,我阅读了更多内容,并做了一些可能对其他人有用的笔记。

默认情况下,单个语句(包括 SELECT)对“已提交”数据(READ COMMITTED)起作用,问题是:它们是否等待数据“空闲”并在读取时阻止其他语句工作?

通过右键单击数据库“属性 -> 选项 -> 杂项”进行设置:

并发/阻塞:读取提交快照是否开启 [默认关闭,应该开启]:

使用 SNAPSHOT 进行选择(读取),不要等待其他人,也不要阻止他们。 无需更改代码即可实现效果操作 ALTER DATABASE <dbName> SET READ_COMMITTED_SNAPSHOT [ON|OFF] SELECT name, is_read_committed_snapshot_on FROM sys.databases

一致性:允许快照隔离 [默认关闭,有争议 – OK 关闭]:

允许客户端跨 SQL 语句(事务)请求 SNAPSHOT。 代码必须请求“事务”快照(如SET TRANSACTION ...ALTER DATABASE <dbName> SET ALLOW_SNAPSHOT_ISOLATION [ON|OFF] SELECT name, snapshot_isolation_state FROM sys.databases

问题是:Read Committed Snapshot 和 Allow Snapshot Isolation 之间不是一个。它们是 Snapshot 的两种情况,可以独立打开或关闭,其中 Allow Snapshot Isolation 是一个高级主题。允许快照隔离允许代码进一步控制快照领域。

如果您考虑一行,问题似乎很清楚:默认情况下,系统没有副本,因此如果其他人正在写入,读取器必须等待,如果其他人正在读取,写入器也必须等待 - 该行必须一直锁定。启用“Is Read Committed Snapshot On”会激活数据库以支持“快照副本”以避免这些锁定。

漫无目的...

在我看来,“Is Read Committed Snapshot On”对于任何普通的 MS SQLServer 数据库都应该是 TRUE,并且默认情况下它提供 FALSE 是一种过早的优化。

但是,有人告诉我,单行锁变得更糟,不仅因为您可能要跨表处理多行,还因为在 SQL Server 中,行锁是使用“块”级锁实现的(锁定与存储接近相关的随机行) 并且存在多个锁触发表锁定的阈值 - 可能是更“乐观”的性能优化,但有可能在繁忙的数据库中出现阻塞问题。

【讨论】:

以上是关于读取提交的快照 VS 快照隔离级别的主要内容,如果未能解决你的问题,请参考以下文章

数据库的快照隔离级别(Snapshot Isolation)

精通Java事务编程-弱隔离级别之快照隔离和可重复读

数据库的快照隔离级别(Snapshot Isolation)

转:nolock的替代方案-提交读快照隔离[行版本控制]

快照隔离级别是不是可以防止幻读?

精通Java事务编程-弱隔离级别之快照隔离和可重复读