我注意到在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可序列化模型是否只提供快照隔离保证,而没有写歪斜异常检测?
更新
我甚至更改了Tx1以发出一个UPDATE语句,该语句更改属于同一个version的所有post_comment记录的post列。
这样,Tx2创建一个新记录,Tx1将在不知道添加了满足更新筛选条件的新记录的情况下提交。
实际上,在PostgreSQL上使它失败的唯一方法是在插入虚拟记录之前,在Tx2中执行以下计数查询:
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提交juts是很好的。
顺便说一下,您可以自己运行所有这些示例,因为它们都在GitHub上。

最佳答案

在1995年的论文中,Jim Gray和co将幻影描述为:
P3:r1[P]…w2[y in P]…(c1或a1)(幻影)
一个重要的注意事项是,ANSI SQL P3只禁止插入(和
根据某些解释,更新)到谓词,而
上面P3的定义禁止任何满足谓词的写操作
一旦谓词被读取-写入可以是插入,
更新或删除。
因此,幻象读取并不意味着您可以简单地返回当前运行事务开始时的快照,并假设为查询提供相同的结果将保护您免受实际幻象读取异常的影响。
在最初的SQL Server 2PL(两阶段锁定)实现中,为查询隐含谓词锁返回相同的结果。
MVCC(多版本并发控制)快照隔离(在Oracle中被错误命名为Serializable)实际上并不阻止其他事务插入/删除与当前运行事务中已执行并返回结果集的查询匹配的筛选条件的行。
因此,我们可以设想以下场景,在该场景中,我们希望向所有员工申请加薪:
Tx1:SELECT SUM(salary) FROM employee where company_id = 1;
Tx2:INSERT INTO employee (id, name, company_id, salary) VALUES (100, 'John Doe', 1, 100000);
Tx1:UPDATE employee SET salary = salary * 1.1;
Tx2:COMMIT;
Tx1:COMMIT:
在这种情况下,CEO运行第一个事务(Tx1),因此:
她首先核对公司所有的工资总额。
与此同时,人力资源部负责第二笔交易(Tx2),因为他们刚刚设法雇用了约翰·多伊,并给了他10万美元的薪水。
首席执行官认为,考虑到工资总额,10%的加薪是可行的,因为他不知道工资总额已经增加了10万英镑。
同时,提交HR事务Tx2。
Tx1已提交。
繁荣!首席执行官已经对一张旧快照做出了决定,给出的加薪幅度可能无法维持目前更新的薪资预算。
您可以在A Critique of ANSI SQL Isolation Levels中查看这个用例的详细说明(有很多图表)。
这是幻影读取还是the following post
根据Write Skew,这是一个幻影读取,因为写入偏差定义为:
A5B Write Skew假设T1读取x和y,这与
C(),然后T2读取x和y,写入x,并提交。然后T1
写y。如果x和y之间有约束,可能是
违反了。就历史而言:
A5B:r1[x]…r2[y]…w1[y]…w2[x]…(出现c1和c2)
在Oracle中,事务管理器可能检测到也可能检测不到上述异常,因为它不使用谓词锁或Jim Gray and co,如MySQL。
只有当Bob对employee表发出read时,PostgreSQL才能捕捉到这种异常,否则,这种现象是无法防止的。
更新
最初,我假设序列化也意味着时间顺序。然而,由于index range locks (next-key locks),壁时钟排序或线性化仅假设为严格的串行化。
因此,我的假设是一个严格的可序列化系统。但这不是Serializable应该提供的功能。可串行化的隔离模型不保证时间,只要操作相当于某个串行执行,就允许重新排序。
因此,根据可序列化的定义,如果第二个事务不发出任何读取,则可能发生这种幻象读取。但是,在一个严格的可序列化模型(2PL提供的模型)中,即使第二个事务没有对我们试图防止幻象读取的相同条目发出读取,幻象读取也会被阻止。

09-10 07:53