最小事务隔离级别,以避免“丢失更新”

时间:2011-11-20 12:19:46

标签: sql-server isolation-level transaction-isolation

使用SQL Server的事务隔离级别,您可以避免某些不需要的并发问题,例如脏读等等。

我现在感兴趣的是丢失更新 - 事实上两个交易可以覆盖彼此的更新,而没有人注意到它。我看到并听到相互矛盾的陈述,至少我必须选择哪种隔离级别来避免这种情况。

Kalen Delaney在她的“SQL Server Internals”一书中说(第10章 - 交易与并发 - 第592页):

在Read Uncommitted隔离中,可以执行前面描述的所有行为,除了丢失的更新

另一方面,一位独立的SQL Server培训师告诉我们,我们至少需要“可重复读取”以避免丢失更新。

那么谁是对的?为什么??

6 个答案:

答案 0 :(得分:10)

我不知道回答为时已晚,但我只是在大学学习交易隔离级别,作为我研究的一部分,我遇到了这个链接:

Microsoft Technet

具体而言,该段落是:

丢失更新

可以通过两种方式之一解释丢失的更新。在第一种情况下,在第一个事务被提交或回滚之前,当一个事务更新的数据被另一个事务覆盖时,认为丢失的更新已经发生。 这种类型的SQL Server 2005中不会发生丢失更新,因为在任何事务隔离级别下都不允许这样做。

丢失更新的另一种解释是当一个事务(事务#1)将数据读入其本地内存,然后另一个事务(事务#2)更改此数据并提交其更改。在此之后,Transaction#1根据在执行Transaction#2之前读入内存的内容更新相同的数据。在这种情况下,可以将Transaction#2执行的更新视为丢失更新。

所以从本质上说,两个人都是对的。

就个人而言(我对错误持开放态度,所以请纠正我,因为我正在学习这个)我从这里得到以下两点:

  1. 事务环境的重点是防止丢失更新,如上一段所述。因此,即使最基本的交易级别不能做到这一点,那么为什么还要使用它。

  2. 当人们谈论丢失的更新时,他们知道第一段适用,所以一般来说意味着第二种类型的丢失更新。

  3. 如果有任何错误,请再次纠正我,因为我也想理解这一点。

答案 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 ...

当然,如果SELECTUPDATE作为单独的操作完成,则需要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 committedrepeatable read之间的唯一区别是,最后一个在第一个事务完成时阻止第二个事务,然后覆盖该值。