我注意到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),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',1); insert into post_comment (post_id,review,id) values (1,'Post comment 1',459,0); insert into post_comment (post_id,'Post comment 2',537,'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,id) VALUES (1,'Phantom',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模型是否仅提供快照隔离保证但没有幻像读取异常检测?
UPDATE
我甚至更改了Tx1以发出UPDATE语句,该语句更改属于同一帖子的所有post_comment记录的版本列.
这样,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,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提交juts很好.
顺便说一下,你可以自己运行所有这些例子,因为它们是在GitHub.