读取已提交的快照 VS 快照隔离级别
有人可以帮助我了解何时在 SQL Server 中使用 SNAPSHOT 隔离级别而不是 READ COMMITTED SNAPSHOT 吗?
我知道在大多数情况下 READ COMMITTED SNAPSHOT 有效,但不确定何时进行 SNAPSHOT 隔离。
谢谢
Could some one please help me understand when to use SNAPSHOT isolation level over READ COMMITTED SNAPSHOT in SQL Server?
I understand that in most cases READ COMMITTED SNAPSHOT works, but not sure when go for SNAPSHOT isolation.
Thanks
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
READ COMMITTED SNAPSHOT 执行乐观读取和悲观写入。相反,SNAPSHOT 执行乐观读取和乐观写入。
Microsoft 建议大多数需要行版本控制的应用使用
READ COMMITTED SNAPSHOT
。阅读这篇优秀的 Microsoft 文章:选择基于行版本控制的隔离级别。它解释了两种隔离级别的优点和成本。
这是更彻底的:
http://msdn.microsoft.com/en-us/库/ms345124(SQL.90).aspx
READ COMMITTED SNAPSHOT
does optimistic reads and pessimistic writes. In contrast,SNAPSHOT
does optimistic reads and optimistic writes.Microsoft recommends
READ COMMITTED SNAPSHOT
for most apps that need row versioning.Read this excellent Microsoft article: Choosing Row Versioning-based Isolation Levels. It explains the benefits and costs of both isolation levels.
And here's a more thorough one:
http://msdn.microsoft.com/en-us/library/ms345124(SQL.90).aspx
[![隔离级别表][2]][2]
请参阅下面的示例:
读取提交的快照
更改数据库属性,如下
会话 1 >
会话 2
结果 – 会话 2 中的查询显示旧值 (1, ONE),因为当前事务未提交。这也是避免阻塞和读取已提交数据的方法。
会话 1
会话 2
结果 – 会话 2 中的查询未显示任何行,因为行已在会话 1 中更新。因此,我们再次看到已提交的数据。
快照隔离级别
这是新的隔离级别,从 SQL Server 2005 开始提供。对于此功能,应用程序需要进行更改,因为它必须使用新的隔离级别。
使用下面的命令更改数据库设置。我们需要确保数据库中没有事务。
结果来更改连接的隔离级别 -
现在,我们还需要使用下面的Session 1
Session 2
即使我们将值更改为 10,我们仍然会看到旧的记录在会话 2 (2, TWO) 中。
现在,让我们在会话 1 中提交事务
会话 1
让我们回到会话 2 并再次运行 select。
会话2
我们仍然会看到该记录,因为会话2已经声明了具有快照隔离的事务。除非我们完成交易,否则我们将看不到最新记录。
会话 2
现在,我们不应该看到该行,因为它已经更新了。
请参阅:SQL 权威,Safari 在线图书< /a>
[![Isolation levels table][2]][2]
See the example below:
Read Committed Snapshot
Change the database property as below
Session 1
Session 2
Result – Query in Session 2 shows old value (1, ONE) because current transaction is NOT committed. This is the way to avoid blocking and read committed data also.
Session 1
Session 2
Result – Query in Session 2 shows no rows because row is updated in session 1. So again, we are seeing committed data.
Snapshot Isolation Level
This is the new isolation level, which was available from SQL Server 2005 onwards. For this feature, there is a change needed in the application as it has to use a new isolation level.
Change database setting using below. We need to make sure that there is no transaction in the database.
Now, we also need to change the isolation level of connection by using below
Session 1
Session 2
Result- Even if we have changed the value to 10, we will still see old record in session 2 (2, TWO).
Now, let’s commit transaction in session 1
Session 1
Let’s come back to session 2 and run select again.
Session 2
We will still see the record because session 2 has stated the transaction with snapshot isolation. Unless we complete the transaction, we will not see latest record.
Session 2
Now, we should not see the row as it's already updated.
See: SQL Authority, Safari Books Online
如果不讨论快照中可能发生的可怕的“快照更新冲突”异常,但提交快照中则不会,那么对快照和提交快照读的比较就不完整。
简而言之,快照隔离在事务开始时检索已提交数据的快照,然后对读取和写入使用乐观锁定。如果在尝试提交事务时发现其他内容更改了部分相同数据,则数据库将回滚整个事务并引发错误,导致调用代码中出现快照更新冲突异常。这是因为受事务影响的数据版本在事务结束时与开始时不同。
快照读取提交不会遇到此问题,因为它使用写入锁定(悲观写入),并且在每个语句开始时获取所有已提交数据的快照版本信息。
快照和非快照读提交中发生快照更新冲突的可能性是两者之间极其显着的区别。
No comparison of Snapshot and Snapshot Read Committed is complete without a discussion of the dreaded "snapshot update conflict" exception that can happen in Snapshot, but not Snapshot Read Committed.
In a nutshell, Snapshot isolation retrieves a snapshot of committed data at the start of a transaction, and then uses optimistic locking for both reads and writes. If, when attempting to commit a transaction, it turns out that something else changed some of that same data, the database will rollback the entire transaction and raise an error causing a snapshot update conflict exception in the calling code. This is because the version of data affected by the transaction is not the same at the end of the transaction as it was at the start.
Snapshot Read Committed does not suffer from this problem because it uses locking on writes (pessimistic writes) and it obtains snapshot version information of all committed data at the start of each statement.
The possibility of snapshot update conflicts happening in Snapshot and NOT Snapshot Read Committed is an extremely significant difference between the two.
仍然相关,从比尔的评论开始,我阅读了更多内容并做了可能对其他人有用的笔记。
默认情况下,单个语句(包括 SELECT)对“已提交”数据(READ COMMITTED)起作用,问题是:它们是否等待数据“空闲”并在读取时阻止其他人工作?
通过右键单击数据库“属性 -> 选项 -> 其他”进行设置:
并发/阻塞:读提交快照是否打开 [默认关闭,应打开]:
ALTER DATABASESET READ_COMMITTED_SNAPSHOT [ON|OFF]
从 sys.databases 中选择名称,is_read_commissed_snapshot_on
一致性:允许快照隔离 [默认关闭,值得商榷 - 确定关闭]:
SET TRANSACTION ...
)ALTER DATABASESET ALLOW_SNAPSHOT_ISOLATION [ON|OFF]
SELECT name, snapshot_isolation_state FROM sys.databases
对于问题:读取提交快照和允许之间不是一个或快照隔离。它们是快照的两种情况,并且可以独立打开或关闭,其中允许快照隔离是一个更高级的主题。允许快照隔离允许代码进一步控制快照区域。
如果你考虑一行,这个问题似乎很清楚:默认情况下,系统没有副本,因此如果其他人正在写入,则读取者必须等待,如果其他人正在读取,写入者也必须等待 - 该行必须锁定所有副本时间。启用“读取已提交快照”会激活数据库支持“快照副本”以避免这些锁定。
胡言乱语...
在我看来,对于任何普通的 MS SQLServer 数据库来说,“读取已提交快照已打开”应该为 TRUE,并且默认情况下它提供的是 FALSE,这是一种过早的优化。
然而,我听说单行锁变得更糟,不仅因为您可能跨表寻址多行,而且因为在 SQL Server 中,行锁是使用“块”级锁(锁定与存储邻近性关联的随机行)来实现的,并且多个锁触发表锁定存在一个阈值 - 可能是更“乐观”的性能优化,但存在繁忙数据库中阻塞问题的风险。
Still relevant, starting with Bill's comments I read more and made notes that might be useful to someone else.
By default single statements (including SELECT) work on "committed" data (READ COMMITTED), the question is: do they wait for data to be "idle" and stop others from working when reading?
Setting via right click DB "Properties -> Options -> Miscellaneous":
Concurrency/Blocking: Is Read Committed Snapshot On [defaults off, should be on]:
ALTER DATABASE <dbName> SET READ_COMMITTED_SNAPSHOT [ON|OFF]
SELECT name, is_read_committed_snapshot_on FROM sys.databases
Consistency: Allow Snapshot Isolation [defaults off, debatable – OK off]:
SET TRANSACTION ...
)ALTER DATABASE <dbName> SET ALLOW_SNAPSHOT_ISOLATION [ON|OFF]
SELECT name, snapshot_isolation_state FROM sys.databases
To the question: it is not one or the other between Read Committed Snapshot and Allow Snapshot Isolation. They are two cases of Snapshot, and either could be on or off independently, with Allow Snapshot Isolation a bit more of an advanced topic. Allow Snapshot Isolation allows code to go a step further controlling Snapshot land.
The issue seems clear if you think about one row: by default the system has no copy, so a reader has to wait if anyone else is writing, and a writer also has to wait if anyone else is reading – the row must lock all the time. Enabling "Is Read Committed Snapshot On" activates the DB to support "snapshot copies" to avoid these locks.
Rambling on...
In my opinion "Is Read Committed Snapshot On" should be TRUE for any normal MS SQLServer databases, and that it is a premature optimization that it ships FALSE by default.
However, I'm told the one row lock gets worse not only because you may be addressing multiple rows across tables, but because in SQL Server row locks are implemented using "block" level locks (locking random rows associated by storage proximity) and that there is a threshold where multiple locks trigger table locking - presumably more "optimistic" performance optimizations at the risk of blocking issues in busy databases.
我来描述一下没有提到的2点。
首先让我们弄清楚如何使用两者,因为它并不直观。
SNAPSHOT 和 READ_COMMITTED_SNAPSHOT 是两个不同的隔离级别。
SNAPSHOT 是您可以像往常一样在事务中显式使用的隔离级别:
READ_COMMITTED_SNAPSHOT 不能这样使用。 READ_COMMITTED_SNAPSHOT 既是数据库级别选项,也是隐式/自动隔离级别。要使用它,您需要为整个数据库启用它:
上面的数据库设置的作用是,每次您运行这样的事务时:
启用此选项后,所有 READ_COMMITTED 事务将在 READ_COMMITTED_SNAPSHOT 隔离级别下运行 强>。这会自动发生,影响将此设置设置为 ON 的数据库发出的所有 READ_COMMITTED 事务。无法在 READ_COMMITTED 隔离级别下运行事务,因为具有此级别的所有事务都将自动转换为 READ_COMMITTED_SNAPSHOT。
其次,您不应该盲目使用 READ_COMMITTED_SNAPSHOT 选项。
为了说明它可能产生的问题,假设您有这样的简单事件表:
并且您使用如下查询定期轮询它:
上面的查询不需要包含在事务和显式隔离级别中。 READ_COMMITTED 是默认隔离级别,如果您调用查询而不将其包装在事务块中 - 它将在 READ_COMMITTED 事务中隐式运行。
您会发现,在 READ_COMMITTED_SNAPSHOT 隔离级别下,自动增量标识值可能会有稍后出现的间隙。
您可以轻松地使用插入来模拟它,如下所示:
...然后是正常插入:
您在 10 秒内调用的轮询函数将返回 id 为 2 的单行。
更新 lastId 后进行轮询将不会返回任何内容。 id 为 1 的行将在 10 秒后出现。
id 为 1 的事件将被有效跳过。
如果您使用 READ_COMMITTED 而没有 READ_COMMITTED_SNAPSHOT 自动升级选项,则不会发生这种情况。
值得理解这个场景。这与 IDENTITY 列不保证唯一性这一事实无关。这与 IDENTITY 列不保证严格的单调性这一事实无关。即使不违反唯一性和严格的单调性,您仍然会遇到间隙 - 可能会在看到具有较低 id 的提交之前看到具有较高 id 的提交。
在 READ_COMMITTED 下不存在此问题。
在 READ_COMMITTED 下,您还可以看到间隙 - 即。通过回滚的事务。但这些差距将是永久性的——即。您不会跳过事件,因为它们永远不会再次出现。 IE。在您看到较高的 id 后,您将不会再看到较低的 id 再次出现。
在打开 READ_COMMITTED_SNAPSHOT 之前,请先了解上述问题及其影响。
对此选项的控制位于开发人员与数据库管理员责任的灰色地带。如果您是管理员,则不应盲目使用它,因为开发人员在开发应用程序时可能依赖于 READ_COMMITTED 隔离语义,而转向 READ_COMMITTED_SNAPSHOT 可能会以非常隐式的、难以发现错误的方式违反这些假设。
另外请注意:
……这很愚蠢,因为开发人员将工作并针对默认情况下不同于打开的内容运行 ci 测试部署的环境。
Let me describe 2 points that have not been mentioned.
Firstly let's make it clear how to use both because it's not intuitive.
SNAPSHOT and READ_COMMITTED_SNAPSHOT are two different isolation levels.
SNAPSHOT is isolation level you can use in your transaction explicitly as usual:
READ_COMMITTED_SNAPSHOT can't be use like this. READ_COMMITTED_SNAPSHOT is both a database level option and an implicit/automatic isolation level. To use it, you need to enable it for the whole database:
What above database setting does, is that every time you run transaction like this:
With this option ON, all READ_COMMITTED transactions will run under READ_COMMITTED_SNAPSHOT isolation level instead. This happens automatically, affecting all READ_COMMITTED transactions issued against database with this setting set to ON. It's not possible to run transaction under READ_COMMITTED isolation level because all transactions with this level will be automatically converted to READ_COMMITTED_SNAPSHOT.
Secondly you shouldn't blindly use READ_COMMITTED_SNAPSHOT option.
To illustrate the kind problems it can create, imagine you have simple events table like this:
And you poll it periodically with query like this:
Above query doesn't need to be enclosed with transaction and explicit isolation level. READ_COMMITTED is default isolation level and if you invoke query without wrapping it in transaction block - it'll be implicitly run in READ_COMMITTED transaction.
You'll find that under READ_COMMITTED_SNAPSHOT isolation level auto-increment identity values may have gaps that later appear.
You can easily simulate it with insert like this:
...followed by normal insert:
Your polling function invoked within 10s will return single row with id 2.
Following poll after updating lastId will return nothing. Row with id 1 had will appear after 10s.
Event with id 1 will be effectively skipped.
This will not happen if you use READ_COMMITTED without READ_COMMITTED_SNAPSHOT auto promotion option.
It's worth understanding this scenario. It's not related to the fact that IDENTITY column doesn't guarantee uniqueness. It's not related to the fact that IDENTITY column doesn't guarantee strict monotonicity. Even when both uniqueness and strict monotonicity are not violated, you still end up with gaps - possibility of seeing commits with higher ids before seeing commits with lower ids.
Under READ_COMMITTED this problem doesn't exist.
Under READ_COMMITTED you can also see gaps - ie. by transactions that rolled back. But those gaps will be permanent - ie. you are not skipping events because they will never reappear. Ie. you won't see lower ids reappearing later after you've seen higher ids.
Please understand above issue and its implications before turning READ_COMMITTED_SNAPSHOT on.
Control of this option lies in the gray area of developer vs db admin responsibility. If you're admin, you should not blindly use it as developers may have relied on READ_COMMITTED isolation semantics when developing application and turning READ_COMMITTED_SNAPSHOT may violate those assumptions in very implicit, hard to find bug way.
Additionally be aware that:
...which is simply put stupid as devs will work and run ci tests against something that is by default different than on deployed environments.