快照隔离好不好?

Gle*_*bka 9 sql-server isolation-level

我的情况:

桌子: User { Id, Name, Stone, Gold, Wood }

我有“”线程:

  • 挖掘线程(每分钟

UPDATE User SET Stone = @calculatedValue WHERE Id=@id UPDATE User SET Wood = @calculatedValue WHERE Id=@id

  • 交易线程(每分钟

UPDATE User SET gold = @calculatedValue WHERE Id=@id

  • 施工线程(每分钟

UPDATE User SET Wood = @calculatedValue WHERE Id=@id UPDATE User SET Stone= @calculatedValue WHERE Id=@id

并有来自用户的“写入”请求:

  • 销售资源

UPDATE User SET Stone(Wood,Gold) = @calculatedValue WHERE Id=@id

(calculatedValue 由C# 业务逻辑代码计算)

在这种情况下,如果我设置read_commited_snapshot隔离级别,我会遇到很多“丢失更新”问题。但是如果我设置了可序列化快照级别,一切正常

我正在查看“隔离级别比较”表,看到可序列化和快照隔离解决了并发事务的所有问题。但是,可序列化非常慢。

  1. 我可以对所有写事务使用快照隔离吗?我不希望我的桌子混乱。我的业务逻辑很难,而且总是在变化。
  2. 快照隔离有缺陷吗?
  3. 什么隔离级别更适合只读事务?

Jim*_*nke 13

概括:

我可以对所有写事务使用快照隔离吗?

是的,但根据使用情况,读取提交的快照可能会更好。

快照隔离有缺陷吗?

是的。它需要存储所有活动事务的行版本,这需要磁盘/内存。

什么隔离级别更适合只读事务?

snapshot isolation,或者read committed snapshot取决于是否有几个语句相互依赖以及事务的大小。read committed如果 tempdb 上有很多更新和磁盘空间是一个问题。

更深入的信息

使用哪种隔离级别取决于您的用例以及您在事务中使用数据的方式。因此,让我们从更深入的级别比较开始。

SQL Server 通过使用两种不同的技术来保持一致性:锁定和行版本控制。

锁定隔离级别

SQL Server 的锁定工作对其读取的表/行发出共享锁,从而阻止其他事务更新数据。更新也是如此,它被发出一个排他锁,阻止其他事务读取数据。锁定发生在数据库的不同部分,我不会详细介绍,例如表、行、索引。

READ COMMITTED使用锁定来确保它只读取提交的数据。并且在读取数据时没有其他事务正在更新数据。这意味着另一个事务当前正在选择的行上的更新语句被阻止。并且,另一个事务正在更新的行上的 select 语句将被阻止,直到提交该数据。

READ UNCOMMITTED忽略所有内容,并且根本不使用任何锁。这意味着另一个事务可以在不被阻塞的情况下对当前正在读取的行执行更新,但它也会导致该事务将接收另一个事务可能尚未提交的数据。

SERIALIZABLE做相反的事情,并锁定一切。READ COMMITTED 在读取行时或根据锁完成语句时释放锁,而 SERIALIZABLE 在事务提交后释放锁。这意味着另一个事务想要更新该事务至少读取过一次的数据,或者另一个事务想要读取该事务已更新的数据将被阻塞,直到该事务被提交。

行版本隔离级别

READ COMMITTED SNAPSHOT 和 SNAPSHOT ISOLATION 改为使用行版本控制。行版本控制意味着每次修改一行时,SQL Server 都会存储该行的一个版本,以确保它在被另一个事务读取时保持不变。

SNAPSHOT ISOLATION 的工作方式是,当对表进行读取时,它会检索事务开始时提交的行的最新版本。这提供了事务内数据的一致快照。事务开始后修改的数据是不可见的,但同时事务不会被阻塞。为了防止丢失更新,如果事务要更新在事务开始后已被另一个事务更改的某些行,它将由于数据冲突而终止事务。

READ COMMITTED SNAPSHOT 的工作方式与快照隔离相同,但它不是将快照保留在整个事务中,而是仅在语句的持续时间内保留它。这意味着一个事务中的两个读取语句可能会收到不同的结果。但是,当事务进行更新时,它使用实际行而不是以前的行版本,并且不会跟踪该行是否已更改。

由于 SQL Server 需要使活动事务可以使用的每个修改行保持可用,因此它将它们存储在 tempdb 中。因此,tempdb 需要足够大以包含所有更改。有一个后台线程检查哪些行仍然需要,并删除其余的行,但如果有一个长时间运行的事务,它将阻止删除这些行。如果 tempdb 空间不足,则不会创建新的行版本,并且任何尝试访问这些(不存在的)行的事务都将终止。

注释

在并发方面,READ COMMITTED SNAPSHOT 是 READ UNCOMMITTED 之外最宽松的。它不会阻塞任何其他 DML 语句,并在每个语句中保持一致的数据视图。

  • SNAPSHOT ISOLATION 是宽松的,并且比 READ COMMITTED SNAPSHOT 保持更好的一致性,缺点是由于其冲突解决在进行更新时可能会失败。同一事务中的多个语句保证彼此一致。

  • READ COMMITTED 允许并发读取,而不是更新。由于锁定,它也比行版本控制级别慢。

  • 不推荐使用 READ UNCOMMITTED 因为它会读取脏数据,如果不用担心,那么最好是因为没有锁定并且不需要保留旧的行版本。

  • 不建议使用 SERIALIZABLE,因为它会锁定所有内容,因此速度慢且不利于并发。

  • 从 sql server 2019 开始,行版本保存在 ADR 的 Persistent Version Store(如果启用了 ADR):https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/sql/snapshot-isolation-在-sql-server (2认同)