postgresql中明显的事务隔离冲突

postgresql中明显的事务隔离冲突,postgresql,transactions,Postgresql,Transactions,我正在CentOS Linux上使用PostgreSQL 9.3.12 我有两个进程连接到同一个数据库,使用默认的事务隔离级别“readcommitted”。据postgres docs称,在提交之前,事务中的一个流程不应“看到”事务中另一个流程所做的更改 我看到的一个序列是: 进程A开始它的事务 进程A删除表T中的所有内容 进程B开始它的事务 进程B尝试选择表T中的一行进行更新 进程B出现空(0行)并调用回滚 进程A从传入数据重新填充表T 进程A提交其事务 现在,表T应该在两个事务开始之前

我正在CentOS Linux上使用PostgreSQL 9.3.12

我有两个进程连接到同一个数据库,使用默认的事务隔离级别“readcommitted”。据postgres docs称,在提交之前,事务中的一个流程不应“看到”事务中另一个流程所做的更改

我看到的一个序列是:

  • 进程A开始它的事务
  • 进程A删除表T中的所有内容
  • 进程B开始它的事务
  • 进程B尝试选择表T中的一行进行更新
  • 进程B出现空(0行)并调用回滚
  • 进程A从传入数据重新填充表T
  • 进程A提交其事务
现在,表T应该在两个事务开始之前填充,进程B的查询应该出现一行。如果这些进程不同时运行,它就会运行

我的理解是,进程B应该在表T中看到所需行的旧副本,并进行更改,而这些更改应该通过进程A删除和重新填充表T来完成。我不明白为什么进程B是空的

除了我自己对这些先决条件的完全误解之外,还有谁能想到我会看到这种行为的另一个原因吗

不要担心糟糕的建筑,它正在消失。我只是想理解为什么这种情况似乎违反了我所理解的“readcommitted”事务隔离

谢谢

根据博士后的文件,交易中的一个流程应该是 不“查看”事务中另一个进程所做的更改,直到 我们已经承诺了


是和否-像往常一样,这取决于。严格地说:

读取提交是PostgreSQL中的默认隔离级别。

当事务使用此隔离级别时,选择查询(不带 FOR UPDATE/SHARE子句(FOR UPDATE/SHARE子句)只查看查询前提交的数据 开始;它从未看到未提交的数据或已提交的更改 通过并发事务执行查询期间。实际上,选择 查询在查询的瞬间看到数据库的快照 开始运行。但是,“选择”确实可以看到上一个选项的效果 在其自身事务中执行的更新,即使它们不是 还没有承诺。还请注意,可以看到两个连续的SELECT命令 不同的数据,即使它们在单个事务中,如果 其他事务在第一次选择开始和结束后提交更改 在第二次选择开始之前

更新、删除,选择更新,然后选择共享命令 在搜索目标行方面的行为与SELECT相同:它们 将仅查找在命令start时提交的目标行 时间但是,这样的目标行可能已经更新(或 被另一个并发事务删除或锁定) 找到了在这种情况下,可能的更新程序将等待第一次更新 正在更新要提交或回滚的事务(如果仍处于 进展)。如果第一个更新程序回滚,则其效果为 否定,第二个更新程序可以继续更新 原来发现了一排。如果第一个更新程序提交,则第二个更新程序 如果第一个更新程序删除了该行,则将忽略该行,否则将忽略该行 尝试将其操作应用于行的更新版本。这个 命令(WHERE子句)的搜索条件将重新计算为 查看行的更新版本是否仍与搜索匹配 条件如果是这样,第二个更新程序将使用 行的更新版本。在选择更新和 选择“共享”,这意味着该行的更新版本 已锁定并返回到客户端

换句话说,简单地选择不同于选择更新/删除/更新。

您可以创建简单的测试用例来观察该行为:

第1课时

test=> START TRANSACTION;
START TRANSACTION
test=> SELECT * FROM test;
 x
----
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
(10 rows)


test=> DELETE FROM test;
DELETE 10
test=>
test=> insert into test select * from generate_series(1,10);
INSERT 0 10
test=> commit;
COMMIT

现在登录另一个会话2:

test=> START TRANSACTION;
START TRANSACTION
test=> SELECT * FROM test;
 x
----
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
(10 rows)


test=> SELECT * FROM test WHERE x = 5 FOR UPDATE;
test=> SELECT * FROM test WHERE x = 5 FOR UPDATE;
 x
---
(0 rows)


test=> select * from test;
 x
----
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
(10 rows)
在最后一个命令
后选择。。。对于更新
session 1“挂起”并正在等待


回到第1课时

test=> START TRANSACTION;
START TRANSACTION
test=> SELECT * FROM test;
 x
----
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
(10 rows)


test=> DELETE FROM test;
DELETE 10
test=>
test=> insert into test select * from generate_series(1,10);
INSERT 0 10
test=> commit;
COMMIT

现在,当您返回到第2课时,您将看到以下内容:

test=> START TRANSACTION;
START TRANSACTION
test=> SELECT * FROM test;
 x
----
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
(10 rows)


test=> SELECT * FROM test WHERE x = 5 FOR UPDATE;
test=> SELECT * FROM test WHERE x = 5 FOR UPDATE;
 x
---
(0 rows)


test=> select * from test;
 x
----
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
(10 rows)

也就是说,简单的
SELECT
仍然看不到任何更改,而
SELECT。。。对于更新
,可以看到行已被删除。但是它没有看到会话1插入的新行

事实上,您看到的序列是:

  • 进程A开始它的事务
  • 进程A删除表T中的所有内容
  • 进程B开始它的事务
  • 进程B尝试选择表T中的一行进行更新
  • 进程B“挂起”并等待会话A执行提交或回滚操作
  • 进程A从传入数据重新填充表T
  • 进程A提交其事务
  • 进程B为空(0行-在会话A提交后)并调用回滚

是否可以使用“截断表”删除所有内容。我认为这不是交易。根据手册:“TRUNCATE不是MVCC安全的。”不,它是从中删除的。您使用的工具是什么,您确定您的流程能够像预期的那样交错工作吗?您正在使用“开始事务”吗?在我看来,B真的不应该在A提交之前看到删除,而我在postgres方面的经验从来没有显示出其他情况。听起来好像进程A实际上处于自动提交模式,您看到的可能与MVCC有关--“每个SQL语句都会看到数据的快照(数据库版本)与前一段时间一样,无论基础数据的当前状态如何,“B选择更新的行在A提交后不存在。相反,t