2012-04-20 53 views
19

我對讀PostgreSQL死鎖有點困惑。PostgreSQL在運行UPDATE時出現死鎖

一個典型的僵局例子是:

-- Transaction 1 
UPDATE customer SET ... WHERE id = 1 
UPDATE customer SET ... WHERE id = 2 

-- Transaction 2 
UPDATE customer SET ... WHERE id = 2 
UPDATE customer SET ... WHERE id = 1 

但如果我更改代碼如下:

-- Transaction 1 
UPDATE customer SET ... WHERE id IN (1, 2) 

-- Transaction 2 
UPDATE customer SET ... WHERE id IN (1, 2) 

將被死鎖在這裏的可能性?

本質上我的問題是:在第二種情況下PostgreSQL會逐一鎖定行,還是鎖定WHERE條件所涵蓋的整個範圍?

在此先感謝!

回答

28

在PostgreSQL中,行會在更新時被鎖定 - 事實上,實際上,每個元組(行的版本)都有一個系統字段0123>來指示哪個事務使得元組當前通過插入或更新)以及稱爲xmax的系統字段來指示哪個事務過期了該元組(通過更新或刪除)。當您訪問數據時,它會檢查每個元組以確定它是否對您的事務可見,方法是檢查您的活動「快照」與這些值。

如果您正在執行UPDATE,並且符合您的搜索條件的元組有一個xmin,它將使您的快照和活動事務的xmax可見,它會阻止,等待該事務完成。如果首次更新元組的事務回滾,則您的事務喚醒並處理該行;如果第一個事務提交,則根據當前事務隔離級別,事務喚醒並採取行動。

顯然,死鎖是由於這種情況發生在不同順序的行上造成的。 RAM中沒有行級鎖,可以同時爲所有行獲取,但如果行按照相同順序更新,則不能進行循環鎖定。不幸的是,建議的IN(1, 2)語法不能保證。不同的會話可能具有不同的成本計算因素,「背景」分析任務可能會在一個計劃生成和另一個計劃生成之間更改統計信息,或者可能使用seqscan並受PostgreSQL優化影響,這會導致新的seqscan加入一個已經在進行中並「循環」以減少磁盤I/O。

如果您以相同的順序,在應用程序代碼或使用遊標中一次更新一個更新,那麼您將只有簡單的阻塞,而不是死鎖。但是,通常情況下,關係數據庫很容易出現序列化錯誤,最好通過一個框架來訪問它們,該框架將根據SQLSTATE來識別它們,並從頭開始自動重試整個事務。在PostgreSQL中,序列化失敗將始終具有40001或40P01的SQLSTATE。

http://www.postgresql.org/docs/current/interactive/mvcc-intro.html

+0

謝謝! 所以我上面的例子可能會導致死鎖(因爲我們不知道在兩個事務中處理行的順序)? – vyakhir 2012-04-20 12:32:17

+0

這可能會導致僵局,儘管這很少見;與第一個例子(明確地選擇不同的順序)相反,它將是常見的。您可以通過在更新表的每個事務期間採取適當強度的表級鎖來排除死鎖,但是這種治療可能比疾病更糟。有關詳細信息,請參閱參考文檔部分。 – kgrittn 2012-04-20 12:49:16

+0

但行更新後PostgreSQL發佈鎖定,但整個UPDATE語句尚未完成? 換句話說,如果我們有一個像 更新...在哪裏ID IN(1,2,3,4,5) 後postgresql更新,比如行id = 2,它會釋放行id = 1嗎?如果是的話,如何在必要時將行回滾? – vyakhir 2012-04-20 13:10:18