2017-02-17 116 views
6

在我的理解中,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 = 2where

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. 

我的問題是:

  1. 爲什麼PostgreSQL的認爲這2個交易衝突?我爲所有SQL添加了user_id條件,並且不修改user_id,但所有這些都不起作用。
  2. 這是否意味着可串行化事務不允許在同一個表上發生併發事務,即使它們的讀/寫沒有衝突?
  3. 爲每個用戶做些事情是非常普遍的,我是否應該避免使用可序列化的事務來處理非常頻繁發生的操作?
+0

您可能想在Postgres郵件列表上詢問這個問題 –

回答

5

您可以用下面的指數修正這個問題:

CREATE INDEX accounts_user_idx ON accounts(user_id); 

由於在您的示例表這麼幾個數據,你必須告訴PostgreSQL的使用索引掃描:

SET enable_seqscan=off; 

現在你的例子將工作!

如果這看起來像黑魔法,請查看您的SELECTUPDATE語句的查詢執行計劃。

如果沒有索引,兩個將在表上使用順序掃描,從而讀取表中的所有行。所以這兩筆交易最終將在整個表格上以SIReadLock結束。

這會觸發序列化失敗。

+0

所以關鍵是要避免全表掃描,並且如果表數據太少,索引不會被觸發,我的理解是否正確? – darkbaby123

+0

完全正確。 –

-2

據我所知serializable具有最高的隔離級別,因此最低級別的併發。事務一個接一個地發生,併發性爲零。

+0

爲什麼PostgreSQL認爲這兩個事務是衝突的?我爲所有SQL添加了user_id條件,並且不修改user_id,但所有這些都不起作用。 - >不知道爲什麼你有這個衝突夥計,也許當它正在尋找記錄,因爲它有一個共同的字段'檢查'可能是一個原因。你有沒有試過'儲蓄'和一個'檢查' – sivalingam

+0

我試過了,仍然有衝突。實際上,即使所有條件都縮減到「where user_id = x」,它也是衝突的。 – darkbaby123