我注意到在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模型是否只提供快照隔离保证而没有写偏斜异常检测?
更新
我甚至更改了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上。