可序列化隔离级别混淆 - 写偏斜Postgres

问题描述

我正在运行 Postgres12 并且对可序列化事务级别的行为感到困惑。

表格:

活动

  1. id
  2. 难度

经理

  1. id
  2. 级别

预期行为(在序列化事务中):

  1. 检查是否有 7 个或更多难度事件=2
  2. 如果是,请插入 level=2 的经理

我在可序列化中运行以下事务,但没有看到我预期的行为(期望可序列化事务检测 2 个会话之间的写入偏差)

-- session 1:
BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE
SELECT count(*) from events WHERE difficulty=2
-- RETURNS 7
-- Now start session 2
-- session 2:
BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE
SELECT id FROM events WHERE difficulty=2 LIMIT 1;
/* 
 id 
----
  4
*/
 UPDATE events SET difficulty=1 WHERE id=4;
COMMIT;

现在只有6个难度=2的事件

-- back in session 1
-- since we have counted 7 events of difficulty=2 in this session,create a manager
INSERT INTO manager (level) VALUES (2);
COMMIT;
-- Expected write skew to be detected here bc the read event rows have seen updates (only 6 actually)

不幸的是,我们的最终状态现在是 6 个难度 = 2 的事件和 2 级经理。 为什么可序列化隔离不能防止这种写倾斜? 我对可序列化隔离用例有什么误解?为什么难度=2 的事件不被谓词锁定或某种隔离机制锁定或监视?

图片清晰

enter image description here

解决方法

SERIALIZABLE 表示有一种方法可以串行执行事务(一个接一个),这样效果是一样的。在您的情况下,这种等效的串行执行将首先运行会话 1,然后运行会话 2,效果相同。

您可以说会话 1 在会话 2 之前“逻辑地”执行。

,

思考后回答我自己的问题!

序列化检查并没有阻止两个会话的提交,因为有可能序列化两个事务并且仍然以 2 级管理器和 6 个难度 = 2 的事件结束。

例如

  1. 运行会话 1(检查是否有 7 个难度=2 的事件,创建经理级别=2)COMMIT;
  2. 运行会话 2(删除一个事件,现在 6 个事件的难度=2)COMMIT;

^输出 = 6 个事件,1 个经理

这与并发运行的结果相同,因此这被视为这两个可序列化事务的“可接受”状态。

如果您想阻止这种行为,可以将会话 2 更新为以下内容

begin transaction isolation level serializable;
select count(*) from manager where level=2; 
--if no managers
update events set difficulty=1 where id=4;

现在没有合乎逻辑的方法来结束具有序列化顺序的状态 6 事件 1 管理器。顺序排序的两种可能结果是

  1. 会话 1 运行
  2. 会话 2 运行

^output = 7 个事件 1 个经理

  1. 会话 2 运行
  2. 会话 1 运行

^output = 6 个事件 0 个经理

因此,在这种情况下(使用更新的会话 2),您的一项交易将被阻止,因为这些交易不再可序列化。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...