簡體   English   中英

為什么 PostgreSQL 會認為兩個可序列化的事務之間存在沖突?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM