在我的理解中,PostgreSQL使用某種監視器來猜測序列化隔離級別是否存在衝突。很多例子都是關於在併發事務中修改相同的資源,而可序列化的事務很有效。但我想用另一種方式來測試併發問題。爲什麼PostgreSQL可序列化事務認爲這是衝突?
我決定測試2個用戶修改自己的帳戶餘額,並希望PostgreSQL足夠聰明,不會將其檢測爲衝突,但結果不是我想要的。
下面是我的表格,有4個賬戶屬於2個用戶,每個用戶都有一個支票賬戶和一個儲蓄賬戶。
create table accounts (
id serial primary key,
user_id int,
type varchar,
balance numeric
);
insert into accounts (user_id, type, balance) values
(1, 'checking', 1000),
(1, 'saving', 1000),
(2, 'checking', 1000),
(2, 'saving', 1000);
表的數據是這樣的:
id | user_id | type | balance
----+---------+----------+---------
1 | 1 | checking | 1000
2 | 1 | saving | 1000
3 | 2 | checking | 1000
4 | 2 | saving | 1000
現在我跑了2級2的用戶併發事務。在每次交易中,我會減少一些錢的支票帳戶,並檢查用戶的總餘額。如果它大於1000,則提交,否則回滾。
用戶1的例子:
begin;
-- Reduce checking account for user 1
update accounts set balance = balance - 200 where user_id = 1 and type = 'checking';
-- Make sure user 1's total balance > 1000, then commit
select sum(balance) from accounts where user_id = 1;
commit;
用戶2是相同的,除了user_id = 2
在where
:
begin;
update accounts set balance = balance - 200 where user_id = 2 and type = 'checking';
select sum(balance) from accounts where user_id = 2;
commit;
我第一次提交用戶1的事務,它成功毫無疑問。當我提交用戶2的交易時,它失敗。
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.
我的問題是:
- 爲什麼PostgreSQL的認爲這2個交易衝突?我爲所有SQL添加了user_id條件,並且不修改user_id,但所有這些都不起作用。
- 這是否意味着可串行化事務不允許在同一個表上發生併發事務,即使它們的讀/寫沒有衝突?
- 爲每個用戶做些事情是非常普遍的,我是否應該避免使用可序列化的事務來處理非常頻繁發生的操作?
您可能想在Postgres郵件列表上詢問這個問題 –