2016-10-17 62 views
1

讀 - 修改 - 寫我們有一個簡單的表的Postgres 9.4死鎖檢測時,對單個表

given_entity{ 
    UUID id; 
    TimeStamp due_time; 
    TimeStamp process_time; 
} 

這是一個使用彈簧數據 - 彈簧啓動(1.2.5.RELEASE)應用程序的應用程序jpa.1.2.5.RELEASE與hibernate-4.3.10.FINAL作爲jpa provier。
我們有5個這個應用程序的實例,每個實例都有一個每2秒運行一次的調度程序,並查詢數據庫中有最後2分鐘的due_time直到現在還沒有處理的行;

SELECT * FROM given_entity 
WHERE process_time is null and due_time between now() and NOW() - INTERVAL '2 minutes' 
FOR UPDATE 

要求是上表的每一行都被恰好一個應用程序實例成功處理。 然後,應用程序實例處理這些行並在一個事務中更新其process_time字段。 這可能需要或可能不會超過2秒,這是調度程序間隔。 此外,我們沒有任何索引,但在此表上的PK索引。 值得注意的第二點是,這些實例可能會插入此表,這是由客戶端單獨調用的。

問題:在日誌中我看到的PostgreSQL此消息(很少,但它發生)

ERROR: deadlock detected 
Detail: Process 10625 waits for ShareLock on transaction 25382449; blocked by process 10012. 
Process 10012 waits for ShareLock on transaction 25382448; blocked by process 12238. 
Process 12238 waits for AccessExclusiveLock on tuple (1371,45) of relation 19118 of database 19113; blocked by process 10625. 
Hint: See server log for query details. 
Where: while locking tuple (1371,45) in relation "given_entity" 

問: 這是如何發生的呢? 我檢查了postgresql鎖並搜索了互聯網。我沒有發現只有一個簡單的表上可能存在死鎖的情況。 我也無法使用測試重現此錯誤。

+0

你可以粘貼代碼更新表的位置嗎? **選擇更新**阻止其他交易的修改,人們應該小心。 –

+0

爲了完全避免這些問題,可以使用Hibernate樂觀鎖定機制(除非有一些真正有效的理由來鎖定您想要更新的記錄)。也許這也可能有幫助:http://blog.2ndquadrant.com/postgresql-anti-patterns-read-modify-write-cycles/ –

+0

爲了更新,我們只需更新process_time字段。像'UPDATE given_entity set process_time = now()where id =?' – Abareghi

回答

1

這很容易發生。

有可能是滿足條件

due_time BETWEEN now() AND now() - INTERVAL '2 minutes' 

,因此它可以很容易地發生的SELECT ... FOR UPDATE發現並鎖定一個行,然後被阻止下一行鎖定幾行。記住–對於死鎖而言,不一定涉及多於一個表,只涉及多於一個可鎖定資源就足夠了。在你的情況下,這些是given_entity表中的兩個不同的行。

甚至可能是兩個SELECT ... FOR UPDATE語句之間發生死鎖。
由於您聲明表中只有主鍵索引,查詢必須執行順序掃描。在PostgreSQL中,從順序掃描返回的行沒有固定順序。相反,如果兩個順序掃描同時運行,則第一個將在第一個掃描儀上捎帶,並且將在第一個順序掃描的當前位置處開始掃描該表。

通過將參數synchronize_seqscans設置爲off並查看死鎖是否消失,您可以檢查是否屬於這種情況。另一種選擇是在運行語句之前對錶進行SHARE ROW EXCLUSIVE鎖定。

+0

非常感謝。我也懷疑掃描行的順序不同。但是由於我不能在100個線程同時寫入和100個線程同時讀取的單元測試中複製它,我不確定是否是這種情況。還有一件事我無法解釋,爲什麼在這裏獲得'AccessExclusiveLock'?那是因爲同時插入會發生,postgresql想要重新建立PK?我不確定'synchronize_sequence = off'會如何影響我們的表現。你認爲搜索欄的索引結果可以減輕這一點嗎? – Abareghi

+0

至於'SHARE ROW EXCLUSIVE'我寧願不去明確鎖定,如果我有其他選擇。 – Abareghi

+0

更重要的是,'SHARE ROW EXCLUSIVE'與其他更多的鎖相沖突,而'ROW SHARE'則是通過'SELECT FOR UPDATE'獲取的。我不確定它是如何影響性能的。 – Abareghi

2

進程A嘗試鎖定第1行,然後鎖定第2行。同時,進程B嘗試鎖定第2行,然後鎖定第1行。這就是觸發死鎖所需的全部操作。

問題是行鎖以不確定的順序獲取,因爲SELECT以不確定的順序返回其行。並避免這僅僅是一個確保所有進程鎖定排在訂單上達成一致,即:

SELECT * FROM given_entity 
WHERE process_time is null and due_time between now() and NOW() - INTERVAL '2 minutes' 
ORDER BY id 
FOR UPDATE 

在Postgres裏9.5+,你可以簡單地忽略它是通過使用FOR UPDATE SKIP LOCKED另一個進程鎖定任何行的事。

+0

非常感謝。所以如果我理解正確,postgresql將會找到'SELECT ...'的結果,然後鎖定結果行。這就是爲什麼你的建議解決方案將避免這個問題? – Abareghi

+0

沒錯,鎖定是在排序後完成的:https://www.postgresql.org/message-id/2382.1170171581%40sss.pgh.pa.us –