【问题标题】:Read Committed vs Read Uncommited if both transaction do not rollback如果两个事务都不回滚,则已提交读与未提交读
【发布时间】:2019-06-01 10:51:50
【问题描述】:

我正在尝试了解已提交的读取和未提交的读取隔离级别。我知道理论上未提交的读取允许脏读,而已提交的读取则不允许,但我仍然无法真正理解。

考虑上图,如果没有任何事务被中止,那么读取已提交和未提交读取的最终结果是否相同?

【问题讨论】:

  • 您好,欢迎来到 StackOverflow。没有人会重新输入您的代码,因此,图像有助于理解订单,但请以文本形式提供。
  • 这里没有什么可重写的,视觉辅助很好,可以理解,代码只是为了解释上下文。当然如果OP包含markdown table会更好。
  • 数据修改语句在读提交和未提交的隔离级别上都获得锁。因此,您的示例中的结果将是相同的。

标签: sql-server isolation-level read-uncommitted read-committed


【解决方案1】:

如果您使用已提交的读取隔离级别,则 T2 需要在第 4 步等待 T1 完成并提交其工作。此外,第 6 步中的 T1 找不到带有 Maria% 的 Nome,因此删除了 0 行。

但是在读取未提交的隔离级别上,两个读/写操作可以同时完成。

结果对于读提交的隔离级别,

Pessoas (Jaoa Silva, 96.....)
Pessoas (Maria Fon..., 9199...)
Pessoas (Joao Manuel Silva, 9699...)

而对于读取未提交的隔离级别

Pessoas (Joao Manuel Silva, 9699...)

【讨论】:

    【解决方案2】:

    READ UNCOMMITTED 允许您读取尚未被其他事务提交的脏数据。 SQL Server 引擎忽略正在读取的表下的任何锁,直接从内存中读取数据。

    READ COMMITTED 将读取已经提交的数据,但如果数据受到其他事务的影响,则会等待。

    因此,在示例中,系统不仅在读取,而且还在尝试删除尚未提交的行,因此,两者都将等待另一个事务完成,因此,该示例是 DEADLOCK 的典型示例。

    为了说明 COMMITTED 与 UNCOMMITTED 之间的区别,我将向您展示一个简单明了的示例,我们将在两种模式下运行两次。

    -- Query Window 1
    SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;  -- Prepare for first Run
    SET TRANSACTION ISOLATION LEVEL READ COMMITTED;    -- Prepare for second Run
    
    BEGIN TRANSACTION                                   -- Step 1
    INSERT INTO Audit (ProductID, PrevValue, NewValue, ChangedBy)   
        VALUES (1, 'AAA', 'aaa', SYSTEM_USER);          -- Step 3
    COMMIT TRANSACTION                                  -- Step 5
    
    -- Query Window 2
    SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;  -- Prepare for first Run
    SET TRANSACTION ISOLATION LEVEL READ COMMITTED;    -- Prepare for second Run
    
    BEGIN TRANSACTION                           -- Step 2
    SELECT * FROM Audit WHERE PrevValue = 'AAA' -- Step 4
    COMMIT TRANSACTION                          -- Step 6
    

    我们必须首先在两个查询中运行 UNCOMMITTED LEVEL 行,然后转到第一个,运行第 1 步,转到第二个,第 2 步,依此类推。 在我们运行第 4 步时的 UNCOMMITTED 中,我们将立即看到结果,因为我们正在进行脏读(从内存中)。 对于第二次运行,我们将首先删除行测试:

    DELETE FROM Audit WHERE PrevValue LIKE 'AAA';   
    

    然后,将在两个查询窗口中运行 COMMITTED LEVEL 行并运行相同的序列。 现在我们将观察到,当我们运行第 4 步时,系统仍然没有响应。就在我们运行第 5 步提交插入的那一刻,窗口将显示结果。

    我希望现在的问题更清楚了。

    【讨论】:

    • 未提交和“在内存中”根本不是一回事。完全有可能在检查点将未提交的数据写入磁盘或从内存中读取已提交的数据
    • 我没有说那是一样的......我只是说,对于提供的示例,两者具有相同的效果,即死锁。无论如何,谢谢你的评论,我会澄清我的回应......
    【解决方案3】:

    您的示例与Isolation Levels 无关。这是因为它们影响readers 的行为,而不是writers,在您的示例中只有writers

    您应该参考这篇 BOL 文章:Understanding Isolation Levels 那说

    选择事务隔离级别不会影响 被获取以保护数据修改。交易总是得到 对它修改的任何数据的排他锁并持有该锁直到 事务完成,无论为 那笔交易。 对于 读取操作事务隔离级别 主要定义免受以下影响的保护级别 其他交易进行的修改。

    在您的示例中,没有任何事务 read,它们都是 modify。第一个事务将在感兴趣的RIDkey 上获取X(取决于表结构,如果是堆表还是聚簇表)——我以后会称之为res_1——用于插入并将在事务的所有期间保持它(它还将在相应的pageobject 上具有IX),对于第二个事务的第一条语句也是如此:它将获取@987654334 @ 在 res_2 上插入。

    DELETE 尝试第二个事务将被阻止,因为它无法获得X(或U,如果where 条件上没有索引),这是因为已经有X on第一个事务持有相同的资源 (res_1)。并且第二个事务中不会有第二个INSERT,因为之前的DELETE被阻塞了。

    最后,当第一个事务尝试它的DELETE时,它需要XU(取决于索引是否存在)在res_2上,但是它已经被tran2用X阻塞了,所以它也被阻塞了并且没有退出这种情况,每个会话都等待另一个会话完成并且没有会话可以完成,此时发生deadlock,服务器将通过rolling back其中一个事务解决它。

    【讨论】:

    • +1 BOL 链接是纯金的。很长一段时间我都没有意识到写锁总是被获取(这让我很困惑,因为我知道它们是需要的不知何故),更重要的是:写锁一直持有到最后交易时间.
    【解决方案4】:

    请找到链接https://www.postgresql.org/docs/9.5/transaction-iso.html

    我正在重写

    13.2.1。读取提交的隔离级别

    Read Committed 是 PostgreSQL 中的默认隔离级别。当事务使用此隔离级别时,SELECT 查询(没有 FOR UPDATE/SHARE 子句)只看到查询前提交的数据 开始;它永远不会看到未提交的数据或已提交的更改 在并发事务的查询执行期间。实际上,一个 SELECT 查询在 查询开始运行。但是,SELECT 确实看到了 以前的更新在它自己的事务中执行,即使它们 尚未承诺。还要注意两个连续的SELECT 命令 可以看到不同的数据,即使它们在一个单一的 事务,如果其他事务在第一个事务之后提交更改 SELECT 开始,在第二个 SELECT 开始之前。

    UPDATEDELETESELECT FOR UPDATESELECT FOR SHARE 在搜索目标方面,命令的行为与 SELECT 相同 行:他们只会找到截至日期提交的目标行 命令开始时间。但是,这样的目标行可能已经 由另一个并发事务更新(或删除或锁定) 它被发现的时间。在这种情况下,可能的更新程序将等待 第一个要提交或回滚的更新事务(如果它仍然是 进行中)。如果第一个更新程序回滚,那么它的效果是 否定,第二个更新程序可以继续更新 最初发现行。如果第一个更新者提交,第二个更新者 如果第一个更新程序删除了该行,将忽略该行,否则它将 尝试将其操作应用于行的更新版本。这 重新评估命令的搜索条件(WHERE 子句) 查看该行的更新版本是否仍然与搜索匹配 健康)状况。如果是这样,第二个更新程序继续使用它的操作 行的更新版本。对于SELECT FOR UPDATESELECT FOR SHARE,表示是该行的更新版本 被锁定并返回给客户端。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-11-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-06-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多