读取提交的快照 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)