繁体   English   中英

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

[英]Read committed Snapshot VS Snapshot Isolation Level

有人可以帮助我了解何时在 SQL 服务器中使用 SNAPSHOT 隔离级别超过 READ COMMITTED SNAPSHOT 吗?

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

谢谢

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

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

阅读这篇优秀的 Microsoft 文章:选择基于行版本控制的隔离级别 它解释了两种隔离级别的好处和成本。

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

在此处输入图片说明 [![隔离级别表][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

第二节

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

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

第一节

COMMIT

第二节

USE SQLAuthority
GO
SELECT *
FROM   DemoTable
WHERE i = 1

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

快照隔离级别

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

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

ALTER DATABASE SQLAuthority SET AllOW_SNAPSHOT_ISOLATION ON

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

第一节

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

第二节

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

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

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

第一节

COMMIT

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

第二节

SELECT *
FROM   DemoTable
WHERE i = 2

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

第二节

COMMIT
SELECT *
FROM   DemoTable
WHERE i = 2

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

请参阅: SQL 权限Safari 联机丛书

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

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

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

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

仍然相关,从比尔的评论开始,我阅读了更多内容并做了可能对其他人有用的笔记。

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

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

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

  • 使用 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 中,行锁是使用“块”级锁(锁定与存储接近度关联的随机行)实现的,而且存在多个锁触发表锁定的阈值 - 可能是更“乐观”的性能优化,但冒着阻塞数据库问题的风险。

让我描述一下没有提到的2点。

首先让我们弄清楚如何使用两者,因为它不直观。

SNAPSHOT 和 READ_COMMITTED_SNAPSHOT 是两个不同的隔离级别。

SNAPSHOT 是您可以像往常一样在事务中显式使用的隔离级别:

begin transaction
set transaction isolation level snapshot;
-- ...
commit

READ_COMMITTED_SNAPSHOT 不能这样使用。 READ_COMMITTED_SNAPSHOT 既是数据库级别选项,也是隐式/自动隔离级别。 要使用它,您需要为整个数据库启用它:

alert database ... set read_committed_snapshot on;

上面的数据库设置所做的是,每次您运行这样的事务时:

begin transaction
set transaction isolation level read committed;
-- ...
commit

启用此选项后,所有 READ_COMMITTED 事务都将在READ_COMMITTED_SNAPSHOT隔离级别下运行。 这会自动发生,影响针对此设置设置为 ON 的数据库发出的所有 READ_COMMITTED 事务。 无法在 READ_COMMITTED 隔离级别下运行事务,因为所有具有此级别的事务都将自动转换为 READ_COMMITTED_SNAPSHOT。

其次你不应该盲目地使用 READ_COMMITTED_SNAPSHOT 选项。

为了说明它可能产生的问题类型,假设您有这样的简单事件表:

create table Events (
  id int not null identity(1, 1) primary key,
  name nvarchar(450) not null
  -- ...
)

然后您定期使用这样的查询对其进行轮询:

begin transaction
set transaction isolation level read committed; -- automatically set to read committed snapshot when this setting is ON on database level 
select top 100 * from Events where id > ${lastId} order by id asc; 
commit

以上查询不需要包含事务和显式隔离级别。 READ_COMMITTED 是默认隔离级别,如果您调用查询而不将其包装在事务块中 - 它将在 READ_COMMITTED 事务中隐式运行。

您会发现,在 READ_COMMITTED_SNAPSHOT 隔离级别下,自动递增的标识值可能会有稍后出现的间隙。

您可以像这样使用 insert 轻松模拟它:

begin transaction
insert into Events (name) values ('test 1');
waitfor delay '00:00:10'
commit

...然后是正常插入:

insert into Events (name) values ('test 2');

您在 10 秒内调用的轮询 function 将返回 ID 为 2 的单行。

更新 lastId 后进行轮询将不返回任何内容。 id 为 1 had 的行将在 10 秒后出现。

id 为 1 的事件将被有效地跳过。

如果您在没有 READ_COMMITTED_SNAPSHOT 自动升级选项的情况下使用 READ_COMMITTED,则不会发生这种情况。

理解这种情况是值得的。 这与 IDENTITY 列不保证唯一性这一事实无关。 这与 IDENTITY 列不保证严格单调性这一事实无关。 即使没有违反唯一性和严格的单调性,您仍然会遇到差距 - 可能会在看到具有较低 ID 的提交之前看到具有较高 ID 的提交。

在 READ_COMMITTED 下这个问题不存在。

在 READ_COMMITTED 下,您还可以看到差距 - 即。 通过回滚的交易。 但这些差距将是永久性的——即。 您不会跳过事件,因为它们永远不会再次出现。 IE。 在看到较高的 ID 之后,您不会再看到较低的 ID。

请在打开 READ_COMMITTED_SNAPSHOT 之前了解上述问题及其影响。

此选项的控制位于开发人员与数据库管理员责任的灰色区域。 如果你是管理员,你不应该盲目地使用它,因为开发人员在开发应用程序时可能依赖 READ_COMMITTED 隔离语义,而转换 READ_COMMITTED_SNAPSHOT 可能会以非常隐含的、难以发现错误的方式违反这些假设。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM