表: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隔离级别,就会出现许多“丢失的更新”问题。但是,如果我设置了可序列化或快照级别,则所有操作都很好。
我看了一个“隔离级别比较”表,发现可序列化和快照隔离解决了并发事务的所有问题。但是,可串行化非常慢。
发布于 2018-08-27 10:57:50
是否可以对所有写事务使用快照隔离?
是的,但是根据使用情况,读提交的快照可能更好。
快照隔离有缺陷吗?
是。它需要存储所有活动事务的行版本,这需要磁盘/内存。
什么隔离级别更适合只读事务?
snapshot isolation,或者read committed snapshot,取决于是否有几个语句依赖于彼此和事务的大小。如果有许多更新和磁盘空间在read committed上是一个问题。
要使用的隔离级别取决于用例以及如何在事务中使用数据。因此,让我们从更深层次的比较开始。
Server通过使用两种不同的技术来维护一致性:锁定和行版本控制。
SQL Server的锁定工作在它读取的表/行上发出共享锁,从而阻止其他事务更新数据。更新也是如此,这些更新被发出独占锁,阻止其他事务读取数据。锁定发生在数据库的不同部分,我将不讨论这些部分,但是,例如表、行、索引。
reads使用锁定来确保它只读取提交的数据。并且在读取数据时,没有其他事务正在更新数据。这意味着另一个事务当前正在选择的行上的update语句将被阻塞。并且,另一个事务正在更新的行上的select语句将被阻塞,直到该数据被提交。
未提交的读取将忽略所有内容,并且根本不使用任何锁。这意味着另一个事务可以对事务当前正在读取的行执行更新,而不会被阻塞,但它也会导致事务接收到其他事务可能尚未提交的数据。
SERIALIZABLE做相反的事情,并锁定一切。READ在读取行时或根据锁完成语句时释放锁,而SERIALIZABLE则在事务提交后释放锁。这意味着,希望更新事务至少读取一次的数据的另一个事务或希望读取事务更新的数据的另一个事务将被阻塞,直到事务提交。
读取提交的快照和快照隔离使用行版本控制。行版本控制意味着每次修改一行时,Server都会存储该行的一个版本,确保它在被另一个事务读取时保持不变。
快照隔离的工作方式是,当对表进行读取时,它检索事务开始时提交的行的最后一个版本。这提供了事务内数据的一致快照。事务启动后修改的数据将不可见,但同时事务不会被阻塞。为了防止丢失的更新,如果事务要更新事务开始后被另一个事务更改的一些行,它将由于数据冲突而终止事务。
读取提交的快照与快照隔离的工作方式相同,但它只在语句的持续时间内保留快照,而不是保持整个事务的快照。这意味着一个事务中的两个read语句可能收到不同的结果。但是,当事务正在进行更新时,它使用的是实际行而不是上一行版本,并且不跟踪该行是否已被更改。
由于Server需要保持活动事务可以使用的每个修改行,所以它将它们存储在tempdb中。因此,tempdb需要足够大来包含所有的更改。有一个后台线程,它检查哪些行仍然需要,并移除其余的行,但是如果有一个长时间运行的事务,它将阻止那些行被删除。如果tempdb将耗尽空间,则不会创建新的行版本,任何试图访问这些(不存在的)行的事务都将终止。
读提交快照与读未提交的快照不同,在并发性方面,读未提交快照是最允许的。它不会阻止任何其他DML语句,并保持每个语句中数据的一致视图。
https://dba.stackexchange.com/questions/212983
复制相似问题