4

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

这个例子

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

4

4 回答 4

6

您的示例与Isolation Levels. 这是因为它们会影响readers行为,而不是writers,并且在您的示例中只有writers.

你应该参考这篇 BOL 文章:Understanding Isolation Levels

选择事务隔离级别不会影响为保护数据修改而获取的锁。无论为该事务设置的隔离级别如何,事务总是对其修改的任何数据获得排他锁并持有该锁直到事务完成。对于 读操作事务隔离级别主要定义免受其他事务修改影响的保护级别。

在您的示例中,没有任何交易read,它们都是modify。第一个事务将获取X感兴趣的RIDkey(取决于表结构,如果它是堆表或聚集表)——我将在未来将其称为res_1——用于插入并将在事务的所有持续时间内保持它(它将也有IXon 对应的pageand object),第二个事务的第一条语句也是如此:插入时会Xres_2上获取。

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

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

于 2019-01-07T10:39:06.113 回答
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 步提交插入的那一刻,窗口将显示结果。

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

于 2019-01-06T16:07:57.407 回答
0

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

我在重写

13.2.1. 读提交隔离级别

Read Committed是 PostgreSQL 中的默认隔离级别。当事务使用此隔离级别时,SELECT查询(不带 FOR UPDATE/SHARE子句)只能看到在查询开始之前提交的数据;它永远不会看到未提交的数据或并发事务在查询执行期间提交的更改。实际上, SELECT查询会在查询开始运行的那一刻看到数据库的快照。但是,SELECT确实会看到在其自己的事务中执行的先前更新的影响,即使它们尚未提交。另请注意,如果其他事务在第一次启动SELECT之后和第二次启动之前提交更改,则两个连续命令可以看到不同的数据,即使它们在单个事务中也是如此 。SELECTSELECT

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

于 2019-07-13T08:36:04.540 回答
-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...)
于 2019-01-06T15:55:51.193 回答