使用SQL Server的事务隔离级别,您可以避免某些不需要的并发问题,例如脏读等等。
我现在感兴趣的是丢失更新 - 事实上两个交易可以覆盖彼此的更新,而没有人注意到它。我看到并听到相互矛盾的陈述,至少我必须选择哪种隔离级别来避免这种情况。
Kalen Delaney在她的“SQL Server Internals”一书中说(第10章 - 交易与并发 - 第592页):
在Read Uncommitted隔离中,可以执行前面描述的所有行为,除了丢失的更新。
另一方面,一位独立的SQL Server培训师告诉我们,我们至少需要“可重复读取”以避免丢失更新。
那么谁是对的?为什么??
答案 0 :(得分:10)
我不知道回答为时已晚,但我只是在大学学习交易隔离级别,作为我研究的一部分,我遇到了这个链接:
具体而言,该段落是:
丢失更新
可以通过两种方式之一解释丢失的更新。在第一种情况下,在第一个事务被提交或回滚之前,当一个事务更新的数据被另一个事务覆盖时,认为丢失的更新已经发生。 这种类型的SQL Server 2005中不会发生丢失更新,因为在任何事务隔离级别下都不允许这样做。
丢失更新的另一种解释是当一个事务(事务#1)将数据读入其本地内存,然后另一个事务(事务#2)更改此数据并提交其更改。在此之后,Transaction#1根据在执行Transaction#2之前读入内存的内容更新相同的数据。在这种情况下,可以将Transaction#2执行的更新视为丢失更新。
所以从本质上说,两个人都是对的。
就个人而言(我对错误持开放态度,所以请纠正我,因为我正在学习这个)我从这里得到以下两点:
事务环境的重点是防止丢失更新,如上一段所述。因此,即使最基本的交易级别不能做到这一点,那么为什么还要使用它。
当人们谈论丢失的更新时,他们知道第一段适用,所以一般来说意味着第二种类型的丢失更新。
如果有任何错误,请再次纠正我,因为我也想理解这一点。
答案 1 :(得分:8)
书中的例子是文员A和文员B接收小部件的货物。
他们都检查当前的库存,看看有库存。职员A有50个小部件,更新到75个,职员B有20个小工具,所以45个更新覆盖了以前的更新。
我认为她意味着职员A在所有隔离级别都可以避免这种现象
UPDATE Widgets
SET StockLevel = StockLevel + 50
WHERE ...
和职员B正在做
UPDATE Widgets
SET StockLevel = StockLevel + 20
WHERE ...
当然,如果SELECT
和UPDATE
作为单独的操作完成,则需要repeatable read
来避免这种情况,因此行上的S
锁定会持续一段时间事务(在这种情况下会导致死锁)
答案 2 :(得分:3)
即使读取和写入位于单独的事务中,也可能发生丢失更新,例如用户将数据读入网页,然后更新。在这种情况下,没有隔离级别可以保护您,尤其是在从连接池重用连接时。我们应该使用其他方法,例如rowversion。 Here is my canned answer.
答案 3 :(得分:1)
我的经验是,Read Uncommitted
您不再“丢失更新”,但您仍然可以“丢失回滚”。 SQL培训师可能是指并发问题,因此您可能寻找的答案是Repeatable Read
。
那就是说,如果有人的经历与此相反,我会非常感兴趣。
答案 4 :(得分:0)
正如Francis Rodgers所指出的,你可以依赖SQL Server实现的是,一旦事务更新了一些数据,每个隔离级别总会发出"更新锁定"数据,并拒绝来自另一个事务的更新和写入,无论它的隔离级别如何。您可以确定这种丢失的更新已被涵盖。
但是,如果情况是事务读取某些数据(隔离级别与可重复读取不同),则另一个事务能够更改此数据并提交更改,如果第一个事务则更新相同的数据,但这一次,根据他所做的内部副本,管理系统无法做任何事情来保存它。
你在这种情况下的答案是在第一次交易中使用可重复读取,或者可能使用第一次交易中的一些读锁而不是数据(我不是以自信的方式真正了解它。我只知道这种锁的存在以及你可以使用它们。也许这将有助于任何对这种方法感兴趣的人Microsoft Designing Transactions and Optimizing Locking)。
答案 5 :(得分:0)
以下是70-762 Developing SQL Databases (p. 212)
的引文:
当两个进程读取相同的消息时,可能会出现另一个潜在的问题 行,然后使用不同的值更新该数据。这可能会发生 如果交易首先将值读入变量,然后使用 稍后在更新语句中添加变量。当这个更新 执行后,另一个事务更新相同的数据。无论哪个 这些事务首先被提交成为丢失的更新,因为它 被另一个事务中的更新替换。你不能使用 隔离级别可以更改此行为,但是您可以编写一个 专门允许丢失更新的应用程序。
因此,在这种情况下,似乎没有隔离级别可以帮助您,因此您需要在代码本身中解决问题。例如:
DROP TABLE IF EXISTS [dbo].[Balance];
CREATE TABLE [dbo].[Balance]
(
[BalanceID] TINYINT IDENTITY(1,1)
,[Balance] MONEY
,CONSTRAINT [PK_Balance] PRIMARY KEY
(
[BalanceID]
)
);
INSERT INTO [dbo].[Balance] ([Balance])
VALUES (100);
-- query window 1
BEGIN TRANSACTION;
DECLARE @CurrentBalance MONEY;
SELECT @CurrentBalance = [Balance]
FROM [dbo].[Balance]
WHERE [BalanceID] = 1;
WAITFOR DELAY '00:00:05'
UPDATE [dbo].[Balance]
SET [Balance] = @CurrentBalance + 20
WHERE [BalanceID] = 1;
COMMIT TRANSACTION;
-- query window 2
BEGIN TRANSACTION;
DECLARE @CurrentBalance MONEY;
SELECT @CurrentBalance = [Balance]
FROM [dbo].[Balance]
WHERE [BalanceID] = 1;
UPDATE [dbo].[Balance]
SET [Balance] = @CurrentBalance + 50
WHERE [BalanceID] = 1;
COMMIT TRANSACTION;
创建表,在单独的查询窗口中执行代码的每个部分。更改隔离级别不会执行任何操作。例如,read committed
和repeatable read
之间的唯一区别是,最后一个在第一个事务完成时阻止第二个事务,然后覆盖该值。