2013-05-31 30 views
1

我們注意到下列情況一個PostgreSQL 9.2服務器上的僵局的一種罕見的發生:Postgres的僵局read_commited隔離

T1開始批量操作:

UPDATE BB bb SET status = 'PROCESSING', chunk_id = 0 WHERE bb.status ='PENDING' 
AND bb.bulk_id = 1 AND bb.user_id IN (SELECT user_id FROM BB WHERE bulk_id = 1 
AND chunk_id IS NULL AND status ='PENDING' LIMIT 2000) 

當T1後幾犯幾百毫秒左右(BB有幾百萬行),多線程開始從BB閱讀項目新交易(每線程一個交易),做一些處理,並與查詢,更新他們的50個左右的批次:

對於選擇:

SELECT *, RANK() as rno OVER(ORDER BY user_id) FROM BB WHERE status = 'PROCESSING' AND bulk_id = 1 and rno = $1 

和更新:

UPDATE BB set datetime=$1, status='DONE', message_id=$2 WHERE bulk_id=1 AND user_id=$3 

(USER_ID,bulk_id唯一性約束)。

由於狀況問題的外部原因,另一個事務T2幾乎在T1已經提交之後執行與T1相同的查詢(初始批處理操作,其中項目被標記爲'PROCESSING')。

UPDATE BB bb SET status = 'PROCESSING', chunk_id = 0 WHERE bb.status ='PENDING' 
AND bb.bulk_id = 1 AND bb.user_id IN (SELECT user_id FROM BB WHERE bulk_id = 1 
AND chunk_id IS NULL AND status ='PENDING' LIMIT 2000) 

然而,儘管這些項目被標記爲「處理中」此查詢死鎖與一些更新(這是分批正如我所說所做)關閉工作線程。據我的理解,這不應該發生在我們使用的READ_COMMITTED隔離級別(默認)上。我確信T1已經提交,因爲工作線程在完成之後執行。

編輯:我應該清楚的一件事是T2開始後T1但提交之前。然而,由於我們在同一行(不受任何上述查詢影響)上使用SELECT for UPDATE獲取的write_exclusive元組鎖,它在運行批量更新查詢之前等待T1提交。

回答

0

當T1幾百毫秒左右後提交(BB有幾百萬行),多線程開始從BB閱讀項目新交易(每線程一個交易),做一些處理,並分批進行更新大約有50左右的查詢:

這讓我想起了一個併發問題。我認爲你最好有一個事務讀取行並將它們交給工作進程,然後在他們回來時分批更新它們。你的根本問題在於,這些行在不確定的狀態下有效地工作,在交易期間持有行等。你必須分別處理回滾等,因此鎖定是一個真正的問題。

現在,如果這種解決方案是不可能的,我會有一個單獨的鎖定表。在這種情況下,每個線程分別旋轉,鎖定鎖定表,聲明一堆行,將記錄插入鎖定表並提交。這樣每個線程都要求記錄。然後他們可以在他們的記錄集上工作,更新它們等。您可能想要一個定期清除舊鎖的進程。

實質上,你的問題是行從狀態A→處理→狀態B,並可能回滾。由於其他線程無法知道正在處理的行和哪些線程,因此無法安全地分配記錄。一種選擇是將模型更改爲:

狀態A - >聲明狀態 - >處理 - >狀態B.但是你必須有一些方法來確保行被有效地分配,並且線程知道哪些行已經被分配給彼此。