可序列化隔离模式可用于在插入相等的 id 时避免竞争条件。因此,create table u(uid int primary key, name text);
如果我们运行两个类似的事务 T1 和 T2:
begin isolation level serializable;
select * from u where uid = 1;
然后继续 T1 和 T2:
insert into u (uid, name) values (1, 'A');
只有第一个成功commit;
,而另一个引发序列化失败。
这是该模式的一个非常简洁的功能,可以处理复杂交易中的唯一密钥违规,而不是诉诸特定的“黑客” insert ... on conflict
。但是,即使 uid 不同,例如uid = 2
and uid = 3
,事务 T1 和 T2仍然无法提交。
怎么可能?假设他们创建不同的谓词 SIReadlocks 并select
使用索引扫描。诀窍在哪里?
原因是表完全是空的。
_bt_first
请参阅中的以下代码src/backend/access/nbtree/nbtsearch.c
:通常,索引扫描会
SIRead
在索引条目应该在的索引页上加锁,但由于索引为空,PostgreSQL 会SIRead
在整个表上加锁。现在,由于两个事务都这样做,因此在其中一个事务结束时会出现序列化错误,因为写入与表范围的读锁冲突。
如果表不为空,您会注意到像这样的并发事务有时会成功,因为它们会影响不同的索引页。如果受影响
uid
的 s 足够接近以至于它们位于同一索引页面上,您仍然会遇到“误报”序列化错误。这是记录在案的:SIReadLock
要查看PostgreSQL 使用哪种类型,请检查pg_locks
.