[英]Why does PostgreSQL serializable transaction think this as conflict?
[英]Why does PostgreSQL think there is a conflict between the two serializable transactions?
我試圖弄清楚 PostgreSQL 中的可序列化隔離級別是如何工作的。 理論上,根據 PostgreSQL 自己的文檔,PostgreSQL 應該足夠聰明,可以以某種方式檢測序列化沖突並自動回滾違規事務。 然而,當我自己嘗試使用可序列化隔離級別時,我偶然發現了很多誤報,並開始懷疑我自己對可序列化概念或 PostgreSQL 對其實現的理解。 您可以在下面找到此類誤報的最簡單示例之一:
create table mytab(
class integer,
value integer not null
);
create index mytab_class_idx on mytab (class);
insert into mytab (class, value) values (1, 10);
insert into mytab (class, value) values (1, 20);
insert into mytab (class, value) values (2, 100);
insert into mytab (class, value) values (2, 200);
表數據如下:
class | value
-------+-------
1 | 10
1 | 20
2 | 100
2 | 200
然后我運行兩個並發事務。 代碼中的Step n
注釋顯示了我執行語句的順序。 遵循https://stackoverflow.com/a/42303225/3249257 的建議,我明確禁用順序掃描以強制 PostgreSQL 使用索引:
SET enable_seqscan=off;
交易A:
begin; -- step 1
select sum(value) from mytab where class = 1; -- step 2
insert into mytab(class, value) values (3, 30); -- step 5
commit; -- step 7
交易乙:
begin; -- step 3
select sum(value) from mytab where class = 2; -- step 4
insert into mytab(class, value) values (4, 300); -- step 6
commit; -- step 8
據我了解,這兩個交易之間不應該有任何沖突。 他們不接觸相同的行。 但是,當我提交第二個事務時,它失敗並顯示以下錯誤:
[40001] ERROR: could not serialize access due to read/write dependencies among transactions
Detail: Reason code: Canceled on identification as a pivot, during commit attempt.
Hint: The transaction might succeed if retried.
這里發生了什么? 我對可序列化隔離級別的理解有缺陷嗎? 這個答案https://stackoverflow.com/a/50809788/3249257 中提到的 PostgreSQL 啟發式算法失敗了嗎?
我PostgreSQL 11.5 on x86_64-apple-darwin18.6.0, compiled by Apple LLVM version 10.0.1 (clang-1001.0.46.4), 64-bit
使用PostgreSQL 11.5 on x86_64-apple-darwin18.6.0, compiled by Apple LLVM version 10.0.1 (clang-1001.0.46.4), 64-bit
。
這里的問題在於 PostgreSQL 使用謂詞鎖 (SIReadLock) 來確定並發事務之間是否存在沖突。 如果您在事務執行過程中運行下面的查詢,您將看到這些鎖:
select relation::regclass, locktype, page, tuple, pid from pg_locks
where mode = 'SIReadLock';
在這種情況下,問題出在mytab_class_idx
索引上的頁面鎖定上。 如果並發事務碰巧為mytab_class_idx
關系的同一頁獲取了鎖,則會發生序列化沖突。 如果他們為不同的頁面獲取鎖,他們都成功提交。
如果像上面的問題一樣沒有足夠的數據,所有行的索引條目將落在同一頁上,然后不可避免地會發生序列化沖突。 對於足夠大的表,序列化沖突很少發生,盡管不是那么罕見。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.