ALLOW_SNAPSHOT_ISOLATION 和 READ_COMMITTED_SNAPSHOT

Tra*_*vis 43 sql-server configuration isolation-level

大部分的论坛和榜样的网上总是建议同时拥有ALLOW_SNAPSHOT_ISOLATIONREAD_COMMITTED_SNAPSHOT设置为ON,每当别人问快照,行版本或类似的问题。

我想这两种设置中的 SNAPSHOT 一词都有些令人困惑。我认为,为了让数据库引擎对 READ_COMMITTED 默认行为使用行版本控制而不是锁定,无论设置如何,数据库READ_COMMITTED_SNAPSHOT都设置为 ON 。ALLOW_SNAPSHOT_ISOLATION

ALLOW_SNAPSHOT_ISOLATION设定被设定为ON只允许快照隔离启动事务(例如SET TRANSACTION ISOLATION级快照)时无论READ_COMMITTED_SNAPSHOT设置。

将这两个设置设置为 ON 的唯一原因是它需要具有 READ COMMITTED 行版本控制 快照隔离。

我的问题是,我的理解在某种程度上是错误的吗?并且这两个设置必须始终一起设置为 ON(特别是对于 READ COMMITTED 行版本控制)?

Ali*_*ghi 29

你的理解是正确的。它确实有点令人困惑。

Kim Tripp(SQL Server 的程序员之一,也是 SQLSkills 的一个组成部分)完全了解了您在有关快照隔离MCM 视频中所说的内容。快进到视频中的 41:45 以进入她回答您问题的部分。

如果您使用请ALLOW_SNAPSHOT_ISOLATION确保您SET TRANSACTION ISOLATION LEVEL SNAPSHOT在您的代码中使用,否则您将无法获得任何好处。

如果设置了SET READ_COMMITTED_SNAPSHOT ON,则无需修改任何代码。MS SQL Server 自动为该表应用快照隔离。

我还没有测试过如果你在代码中要求不同的隔离级别会发生什么,我怀疑它会覆盖这个选项但首先测试它。

使用快照隔离快速查看性能开销。

关于快照隔离如何改变应用程序预期行为的好文章。它显示了更新语句和选择语句如何返回完全不同和意外结果的示例。

  • 这是一个很好的答案,我只想澄清几个项目。首先,如果您只是扫描视频,请从 23:18 和 41:45 开始。早期的时间增加了更多的细节。虽然 Kim 提到了原始问题的答案,但如果同时使用,仍然需要修改代码。Read_Committed_Snapshot 是语句级隔离,也就是说只适用于当前运行的语句。Allow_Snapshot_Isolation 是事务级隔离,Begin Tran 和 Commit 之间的一切。它们可以单独使用,但每行的 14 字节开销是相同的。 (4认同)

Tra*_*vis 16

好的,回家测试。这是观察。

CREATE DATABASE TEST;
GO
CREATE TABLE TABLE1
(
    ID tinyint,
    Details varchar(10)
);
GO
INSERT INTO TABLE1
VALUES (1, 'Original');
GO

SELECT
    name,
    snapshot_isolation_state_desc,
    is_read_committed_snapshot_on
FROM sys.databases
WHERE name = 'TEST';
GO
Run Code Online (Sandbox Code Playgroud)

第一次测试,确认两个设置都关闭。

查询 1

USE TEST;

BEGIN TRAN
UPDATE TABLE1
SET Details = 'Update'
WHERE ID = 1;

--COMMIT;
--ROLLBACK;
GO
Run Code Online (Sandbox Code Playgroud)

查询 2

USE TEST;

SELECT ID, Details
FROM TABLE1
WHERE ID = 1;
GO
Run Code Online (Sandbox Code Playgroud)

在此测试中,查询 2 正在等待查询 1 提交,dm_tran_locks DMV 显示查询 1 导致对 TABLE1 的排他锁。

USE TEST;

SELECT
    DB_NAME(tl.resource_database_id) AS DBName,
    resource_type,
    OBJECT_NAME(resource_associated_entity_id) AS tbl_name,
    request_mode,
    request_status,
    request_session_id
FROM sys.dm_tran_locks tl
WHERE 
    resource_database_id = db_id('TEST')
    AND resource_type = 'OBJECT'
Run Code Online (Sandbox Code Playgroud)

第二个测试,回滚先前的事务,设置 READ_COMMITTED_SNAPSHOT ON 但离开 ALLOW_SNAPSHOT_ISOLATION OFF。

ALTER DATABASE TEST
SET READ_COMMITTED_SNAPSHOT ON
WITH ROLLBACK IMMEDIATE;
GO
Run Code Online (Sandbox Code Playgroud)

运行查询 1,然后运行查询 2。DMV 显示查询 1 导致排他锁,但查询 2 返回带有“原始”的详细信息,而没有查询 1 提交事务。READ_COMMITTED 行版本控制似乎已经到位。

添加SET TRANSACTION ISOLATION LEVEL SNAPSHOT;查询 1 和查询 2,并运行查询 1 或查询 2 返回错误 - 快照隔离事务访问数据库 'TEST' 失败,因为该数据库中不允许快照隔离。使用 ALTER DATABASE 允许快照隔离。

第三个测试,回滚之前的事务。设置 READ_COMMITTED_SNAPSHOT OFF 和 ALLOW_SNAPSHOT_ISOLATION ON。

ALTER DATABASE TEST
SET READ_COMMITTED_SNAPSHOT OFF
WITH ROLLBACK IMMEDIATE;
GO

ALTER DATABASE TEST
SET ALLOW_SNAPSHOT_ISOLATION ON;
GO
Run Code Online (Sandbox Code Playgroud)

运行查询 1,然后运行查询 2。DMV 显示查询 1 引起的排他锁。查询 2 似乎正在等待查询 1 完成。打开 ALLOW_SNAPSHOT_ISOLATION ON 似乎不会启用 READ COMMITTED 行版本控制。

添加SET TRANSACTION ISOLATION LEVEL SNAPSHOT;到查询 1 和查询 2。运行查询 1,然后运行查询 2。虽然 DMV 显示查询 1 导致排他锁,但查询 2 返回带有“原始”的详细信息。快照隔离似乎已经到位。

从测试中观察到,READ_COMMITTED_SNAPSHOT无论ALLOW_SNAPSHOT_ISOLATION设置如何,它本身都会启用/禁用 READ COMMITTED 行版本控制,反之亦然。


小智 7

你的理解是正确的。我喜欢从短,干净,简单的定义在这里

当 READ_COMMITTED_SNAPSHOT 数据库选项为 ON 时,设置读已提交隔离级别的事务使用行版本控制。

当 ALLOW_SNAPSHOT_ISOLATION 数据库选项为 ON 时,事务可以设置快照隔离级别。

似乎很多误解来自 MS 本身。例如,他们在这里说:

如果将 READ_COMMITTED_SNAPSHOT 数据库选项设置为 ON,则数据库引擎默认使用行版本控制和快照隔离,而不是使用锁来保护数据。

但是所提到的“快照隔离”并不等于set transaction isolation level snapshot所应用的事务的行为。

至于区别,这里有很好的解释。

如果 READ_COMMITTED_SNAPSHOT 被命名为 READ_COMMITTED_ROW_VERSIONING 或类似的东西,可能会更好。:)