【发布时间】:2017-01-26 19:04:24
【问题描述】:
我注意到 Oracle 和 PostgreSQL 中都出现了以下情况。
考虑到我们有以下数据库架构:
create table post (
id int8 not null,
title varchar(255),
version int4 not null,
primary key (id));
create table post_comment (
id int8 not null,
review varchar(255),
version int4 not null,
post_id int8,
primary key (id));
alter table post_comment
add constraint FKna4y825fdc5hw8aow65ijexm0
foreign key (post_id) references post;
有以下数据:
insert into post (title, version, id) values ('Transactions', 0, 1);
insert into post_comment (post_id, review, version, id)
values (1, 'Post comment 1', 459, 0);
insert into post_comment (post_id, review, version, id)
values (1, 'Post comment 2', 537, 1);
insert into post_comment (post_id, review, version, id)
values (1, 'Post comment 3', 689, 2);
如果我打开两个单独的 SQL 控制台并执行以下语句:
TX1: BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
TX2: BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
TX1: SELECT COUNT(*) FROM post_comment where post_id = 1;
TX1: > 3
TX1: UPDATE post_comment SET version = 100 WHERE post_id = 1;
TX2: INSERT INTO post_comment (post_id, review, version, id) VALUES (1, 'Phantom', 0, 1000);
TX2: COMMIT;
TX1: SELECT COUNT(*) FROM post_comment where post_id = 1;
TX1: > 3
TX1: COMMIT;
TX3: SELECT * from post_comment;
> 0;"Post comment 0";100;1
1;"Post comment 1";100;1
2;"Post comment 2";100;1
1000;"Phantom";0;1
正如预期的那样,SERIALIZABLE 隔离级别保留了 TX1 事务开始时的快照数据,而 TX1 仅看到 3 条 post_comment 记录。
由于Oracle和PostgreSQL中的MVCC模型,允许TX2插入新记录并提交。
为什么允许 TX1 提交?因为这是一个写入偏斜异常,所以我希望看到 TX1 会因“序列化失败异常”或类似情况而回滚。
PostgreSQL 和 Oracle 中的 MVCC Serializable 模型是否只提供快照隔离保证而没有 Write Skew 异常检测?
更新
我什至更改了 Tx1 以发出一条 UPDATE 语句,该语句更改了属于同一 post 的所有 post_comment 记录的 version 列。
这样,Tx2 会创建一条新记录,而 Tx1 将在不知道已添加满足 UPDATE 过滤条件的新记录的情况下提交。
实际上,在 PostgreSQL 上使其失败的唯一方法是,在插入幻像记录之前,我们在 Tx2 中执行以下 COUNT 查询:
Tx2: SELECT COUNT(*) FROM post_comment where post_id = 1 and version = 0
TX2: INSERT INTO post_comment (post_id, review, version, id) VALUES (1, 'Phantom', 0, 1000);
TX2: COMMIT;
然后 Tx1 将被回滚:
org.postgresql.util.PSQLException: ERROR: could not serialize access due to read/write dependencies among transactions
Detail: Reason code: Canceled on identification as a pivot, during conflict out checking.
Hint: The transaction might succeed if retried.
写偏斜异常预防机制很可能检测到此更改并回滚事务。
有趣的是,Oracle 似乎并没有受到这种异常的困扰,因此 Tx1 只是成功提交。由于 Oracle 不会阻止写入倾斜的发生,因此 Tx1 提交很好。
顺便说一句,您可以自己运行所有这些示例,因为它们位于 GitHub。
【问题讨论】:
-
TX2 提交,因为它是第一个更改数据并提交的。 TX1 不会更改相同的数据,因此不需要抛出任何异常,因为可以构建包括 TX1 在内的交易的序列化时间线。如果 TX1 会更改相同的数据或依赖于您的数据的数据,则会引发错误。
-
那我试试读写事务。
-
我在 TX1 中添加了一条 UPDATE 语句,但 Tx1 仍然能够提交。
-
@Vlad:这种行为与您运行 TX1 然后运行 TX2 所看到的完全一致。换句话说,交易已经成功序列化;没有序列化失败。 the Postgres wiki 上有很多很好的示例,它们可能会让您了解应该在何处/何时/为什么会发生序列化错误。
-
这是有道理的。如果 Tx1 和 Tx2 完全一个接一个地运行,我们将得到相同的结果。
标签: oracle postgresql transactions serializable acid