2012-10-12 10 views
2

下面是表(簡體):Postgresql - >來自簡單更新的死鎖。我不能讓事業

 
             Table "public.link" 
    Column  |   Type    |      Modifiers      
---------------+-----------------------------+--------------------------------------------------- 
id   | integer      | not null default nextval('link_id_seq'::regclass) 
page_id  | integer      | 
placed_at  | timestamp without time zone | default now() 
Indexes: 
    "link_pkey" PRIMARY KEY, btree (id) 
    "link_page_id_index" btree (page_id) 
Foreign-key constraints: 
    "link_page_id_foreign_key" FOREIGN KEY (page_id) REFERENCES page(id) ON UPDATE RESTRICT ON DELETE RESTRICT 

這裏是查詢(簡體):

 
UPDATE link SET page_id = ?, placed_at = now() 
WHERE id IN (SELECT id FROM link ...) AND page_id IS NOT NULL

死鎖消息:

 
ERROR: deadlock detected 
    Detail: Process 5822 waits for ShareLock on transaction 19705; blocked by process 5821. 
Process 5821 waits for ShareLock on transaction 19706; blocked by process 5822. 
    Hint: See server log for query details. 

如何可以通過多個進程並行執行的查詢導致死鎖?
謝謝!

+0

行什麼是你的選擇標準爲內部選擇? –

+0

@Germann Arlington,它是從SELECT鏈接JOIN到另一個表。但它有道理嗎?選擇不會導致死鎖,更新的問題。 –

+0

根據事務隔離級別,'SELECT'可能被更新阻塞。如果您的內部選擇受到您正在更新的任一列的影響,那麼您遇到了問題。順便說一句:依賴可能通過'JOIN',而不僅僅是直接'WHERE'子句... 順便說一句:你爲​​什麼更新你的'page_id'呢?這是一個外鍵,因此更新也可能有更深的副作用。 –

回答

7

會話A試圖更新ID 10,2,30,圖4和會話乙與40,30,20嘗試,10

它們都試圖鎖定各自的行準備用於更新和A獲得10並且是等待30,而B獲得30,並等待10。僵局。

您的根本問題是您正在嘗試更新(一些)併發事務中相同的ID。

不知道你的數據庫結構和你想要做什麼,很難建議最好的解決方案。通常,您可以確保不同的後端不更新相同的行,或者減少超時並在隨機暫停後重試。

+2

如果事實證明是這樣(很難說給過於簡單的問題),那麼一種解決方法可以是使用'SELECT ... ORDER BY ... FOR UPDATE'來以確定性順序獲取行鎖,然後才能做這些行的'UPDATE'。 –

+0

@Richard Huxton 你錯了。我不想用相同的ID更新鏈接,請看WHERE子句:「AND page_id IS NOT NULL」。 –

+1

@Oleg - 當然你是。你認爲會話B可以展望未來,並看到會話A將更新與它相同的行嗎?您的page_id更新對B不可見,直到A提交(並且可能晚於取決於事務隔離)。 –

0

在大多數情況下,僵局的到來,因爲行之間循環等待將要更新,所以如果ü要解決死鎖,你可以簡單的使用順序上要更新

UPDATE link SET page_id = ?, placed_at = now() 
WHERE id IN (SELECT id FROM link ... order by page_id) AND page_id IS NOT NULL 
+0

http://stackoverflow.com/help/how-to-answer –