更多示例
除了AH 的回答中提供的示例之外,这里还有一些可能感兴趣的最小示例。我正在使用这个测试设置来方便地在 PostgreSQL 13.5 上运行它们。
常用设置:
0: CREATE TABLE "MyInt"(i INTEGER);
0: INSERT INTO foo VALUES(0);
我想指出的第一件事是,即使是只读语句SELECT
也很重要。
例如,此示例不会引发任何错误:
0: BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ
1: BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ
0: UPDATE "MyInt" SET i = 1
0: COMMIT
1: UPDATE "MyInt" SET i = 2
1: COMMIT
此示例与上述答案中的示例之间的唯一区别在于,该答案中的示例是在这一个线程中,线程 1 在线程 0UPDATE
之后执行COMMIT
。 TODO 为什么这很重要?
但是,如果我们SELECT
在线程 0 提交之前简单地从线程 1 添加一个,如下所示:
0: BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ
1: BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ
0: UPDATE "MyInt" SET i = 1
1: SELECT * FROM "MyInt"
0: COMMIT
1: UPDATE "MyInt" SET i = 2
1: COMMIT
然后最后一个UPDATE
爆炸:
could not serialize access due to concurrent update
TODO 准确了解SELECT
PostgreSQL 为何如此重要/如何跟踪它。似乎发生的是 PostgreSQL 考虑了类型:
如果客户有这样那样的信息,这样的数据库序列化属性可能会被违反吗?
这要求它还跟踪SELECT
语句。
仅在SERIALIZABLE
这是另一个作用于两个不同行的有趣示例。此示例仅在 上炸毁SERIALIZABLE
,但在REPEATABLE READ
(TODO 为什么)上没有炸毁:
INSERT INTO "MyInt" VALUES (0)
INSERT INTO "MyInt" VALUES (10)
BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE
UPDATE "MyInt" SET i = 1 WHERE i = 0
SELECT * FROM "MyInt"
COMMIT
UPDATE "MyInt" SET i = 11 WHERE i = 10
COMMIT
这种情况下的错误信息是:
could not serialize access due to read/write dependencies among transactions
不幸的是,在盯着https://www.postgresql.org/docs/13/transaction-iso.html#XACT-REPEATABLE-READ几个小时后,我仍然无法准确解释为什么其中一些会爆炸而其他人不会,但我觉得这些示例无论如何都足以引起人们的兴趣,也许有人可以澄清 PostgreSQL 在未来的编辑/评论中采取的确切步骤顺序。
测试示例
不出所料,许多此类示例的另一个来源是src/test/isolation下的树内测试,它们也非常易读。一个 grepcould not serialize
src/test/isolation/specs/*.spec下的文件确定要执行哪些步骤,src/test/isolation/expected/*.out下的相应文件包含确切的预期psql
原始输出。因此,我们只需阅读.out
文件即可查看许多完整的失败示例。
例如src/test/isolation/expected/insert-conflict-do-nothing-2.out包含:
starting permutation: beginrr1 beginrr2 donothing1 donothing2 c1 c2 show
step beginrr1: BEGIN ISOLATION LEVEL REPEATABLE READ;
step beginrr2: BEGIN ISOLATION LEVEL REPEATABLE READ;
step donothing1: INSERT INTO ints(key, val) VALUES(1, 'donothing1') ON CONFLICT DO NOTHING;
step donothing2: INSERT INTO ints(key, val) VALUES(1, 'donothing2'), (1, 'donothing3') ON CONFLICT DO NOTHING; <waiting ...>
step c1: COMMIT;
step donothing2: <... completed>
ERROR: could not serialize access due to concurrent update
step c2: COMMIT;
step show: SELECT * FROM ints;
key|val
---+----------
1|donothing1
(1 row)
唯一没有清楚显示的是表创建语句,我们可以在相应的.spec
文件src/test/isolation/specs/insert-conflict-do-nothing-2.spec 中看到:
CREATE TABLE ints (key int, val text, PRIMARY KEY (key) INCLUDE (val));
从中我们了解到,这INSERT INTO ON CONFLICT DO NOTHING
也可能导致序列化失败。
Some of the tests also link to a paper: https://www.cs.umb.edu/~poneil/ROAnom.pdf A Read-Only Transaction Anomaly Under Snapshot Isolation. You know a software is serious when the bug reports are addressed by papers.