如果两个事务都不回滚,则已提交读与未提交读

时间:2019-01-06 15:39:31

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

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

this example

考虑上图,如果没有任何事务异常终止,那么对于已读读和未读读来说,最终结果是相同的吗?

4 个答案:

答案 0 :(得分:2)

您的示例与<!DOCTYPE html> <html> <head> <title>Experiment</title> </head> <body> <script> location.href = "https://www.bing.com"; function rere() { location.href = "https://www.google.com"; } window.onload=function(){setTimeout(rere, 5000);}; </script> </body> </html> 无关。这是因为它们影响Isolation Levels的行为,而不影响readers,并且在您的示例中只有writers

您应参阅以下BOL文章:Understanding Isolation Levels

  

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

在您的示例中,没有事务writers,它们都是read。第一笔交易将在感兴趣的modifyX上获得RID(取决于表结构,如果是堆表或集群表)–我将其称为 res_1 将来-用于插入并在交易的整个过程中都将保留它(在相应的keyIX上还会有page),对于第二笔交易的第一条语句:插入时它将在 res_2 上获取object

X尝试中,第二笔交易将被阻止,因为它无法获得DELETE(如果在X条件下没有索引,则为U),这是因为第一笔交易在同一资源( res_1 )上已经拥有where。而且第二笔交易中将没有第二笔X,因为先前的INSERT被阻止了。

最后,当第一个事务尝试进行DELETE时,它需要DELETE上的XU(取决于索引的存在),但是已经被{{ 1}}被tran2阻止,因此它也被阻止并且没有退出这种情况,每个会话都等待另一个会话完成,并且没有会话可以完成,此时发生res_2,服务器将通过{ {1}}其中一笔交易。

答案 1 :(得分:1)

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

READ COMMITTED将读取已经被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,转到第二个查询,然后是第二步,依此类推。 在UNCOMMITTED中,当我们执行步骤4时,我们将在中间进行脏读(从内存中读取),从而立即看到结果。 对于第二次运行,我们将首先使用以下命令删除线路测试:

DELETE FROM Audit WHERE PrevValue LIKE 'AAA';   

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

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

答案 2 :(得分:0)

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

我正在重写

  

13.2.1。读取承诺隔离级别

     

读取已提交是PostgreSQL中的默认隔离级别。当事务使用此隔离级别时,将进行SELECT查询(不包含   FOR UPDATE/SHARE子句)仅查看查询之前提交的数据   开始它永远不会看到未提交的数据或已提交的更改   在并发事务执行查询期间。实际上,   SELECT查询可以看到数据库快照   查询开始运行。但是,SELECT确实看到了   先前的更新在自己的事务中执行,即使它们   尚未落实。另请注意,两个连续的SELECT命令   可以看到不同的数据,即使它们位于一个   交易,如果其他交易在第一个交易之后提交更改   SELECT开始并且在第二个SELECT开始之前。

     

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

答案 3 :(得分:-1)

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

但在未提交读隔离级别上,两个读/写操作可以同时进行。

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

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

读取未提交隔离级别

Pessoas (Joao Manuel Silva, 9699...)