2013-08-30 70 views
4

我想更好地理解在postgres中鎖定的機制。涉及外鍵約束的死鎖

假設樹可以有蘋果(通過蘋果桌上的外鍵)。看起來,當選擇一棵樹來更新鎖定是在蘋果上獲得的。但是,即使其他人已經在這個蘋果上持有鎖,該操作也不會被阻止。

這是爲什麼?

p.s.請不要建議刪除「選擇更新」。

方案

Transaction 1  Transaction 2 
BEGIN    . 
update apple;  . 
.     BEGIN 
.     select tree for update; 
.     update apple; 
.     --halts because of the other transaction locking an apple 
update apple;  . 
-- deadlock  . 
        COMMIT 
        --transaction succeeds 

代碼

如果你想嘗試一下在你的Postgres - 這裏是你可以複製代碼/粘貼。

我有以下DB模式

CREATE TABLE trees (
    id  integer primary key 
); 

create table apples (
    id  integer primary key, 
    tree_id integer references trees(id) 
); 

和非常簡單的數據

insert into trees values(1); 
insert into apples values(1,1); 

有兩個簡單的交易。一個是更新蘋果,另一個是鎖定一棵樹並更新一個蘋果。

BEGIN; 
    UPDATE apples SET id = id WHERE id = 1; 
    -- run second transaction in paralell 
    UPDATE apples SET id = id WHERE id = 1; 
COMMIT; 

BEGIN; 
    SELECT id FROM trees WHERE id = 1 FOR UPDATE; 
    UPDATE apples SET id = id WHERE id = 1; 
COMMIT; 

當我運行它們時 - 死鎖發生在第一次事務的第二次更新時。

ERROR: deadlock detected 
DETAIL: Process 81122 waits for ShareLock on transaction 227154; blocked by process 81100. 
Process 81100 waits for ShareLock on transaction 227153; blocked by process 81122. 
CONTEXT: SQL statement "SELECT 1 FROM ONLY "public"."trees" x WHERE "id" OPERATOR(pg_catalog.=) $1 FOR SHARE OF x" 
+0

這是哪一個版本?它看起來不像'ShareLock'在最新版本中使用。第二次交易中需要「更新」嗎? –

回答

8

只是胡亂猜測:你正在運行到與實現的細節問題...

具體來說,您select tree for update聲明獲得樹上的排它鎖。並且update apples聲明獲取相關蘋果的排他鎖。

當您在蘋果上運行更新時,Postgres的每行外鍵相關觸發器觸發,以確保存在tree_id。我不記得他們的名字精確了我的頭頂,但他們的產品目錄和有星星點點的文件中引用它們顯式或隱式,如:

create constraint trigger ... on ... from ... 

http://www.postgresql.org/docs/current/static/sql-createtrigger.html

無論如何,這些觸發器將運行的東西,相當於如下:

select exists (select 1 from trees where id = 1); 

,這其中就有你的問題:由於select for update獨佔訪問使得它等待事務2將發佈在樹上鎖爲了最終確定其關於蘋果的更新聲明,但事務2正在等待事務1完成,以獲得對蘋果的鎖定,以便開始對蘋果進行更新聲明。

因此,Postgres陷入僵局。

+2

我相信這是正確的答案。在事務2中,解決方法是在樹行上對共享鎖執行此操作,從而給出一個非排它鎖,以確保沒有人刪除或更新該行:「SELECT id FROM trees WHERE id = 1 FOR SHARE ''。如果你在Postgres 9.3上,你可以使用「FOR KEY SHARE」版本,並且樹上的任何更新都應該使用「FOR NO KEY UPDATE」(假設它們不更改FK) - 更多併發。請參閱http://michael.otacoo.com/postgresql-2/postgres-9-3-feature-highlight-for-key-share-and-for-no-key-update/ – RichVel

+2

@RichVel這與附加值你的評論,應該是被接受的答案。 –

-3

它看起來像索引鎖並不持續整個交易的持續時間。我認爲主要問題是交易1的執行次數相同,爲UPDATE兩次,但需要獲得更多的鎖才能完成第二次執行UPDATE

根據docs,索引鎖只能保持很短的時間。與數據鎖不同,它們不會在事務完成之前保留。讓我們更詳細地看看時間線。

交易1執行第一個UPDATE。這會在​​的行中獲取行級鎖。在操作過程中,它還獲取trees中索引的鎖定。該事務尚未提交,因此行級數據鎖仍由轉換1保留。但是,trees上的索引鎖立即釋放。不知道爲什麼Postgres會爲所有索引類型執行此操作。

交易2出現並鎖定trees進行更新。這會同時鎖定數據和索引。這不會阻止,因爲事務1已經釋放索引鎖定。這次,這兩個鎖都保持到交易結束。不知道爲什麼這個索引鎖定在另一個被釋放時被保持。

交易1回來並嘗試再次UPDATE。​​的鎖定沒有問題,因爲它已經有了。然而,從trees鎖定,因爲事務2已經存在。

在事務2中添加UPDATE使其在事務1上等待,導致死鎖。

編輯:

我回來了,現在我已經安裝的Postgres調查這更多一些。這實際上很奇怪。我看着pg_locks提交事務後2

事務1具有以下鎖:

  • RowExclusive上apples_pkey和蘋果
  • 獨家它的transactionId和virtualxid

事務2有以下鎖(以及其他許多不相關的鎖):

  • AccessShare上trees_pkey
  • RowShare樹木在蘋果

事務元組

  • 獨家它的transactionId和virtualxid
  • RowExclusive上apples_pkey和蘋果
  • 獨家2也等待獲取對交易1的分享鎖。

    有趣的是,two transactions can hold a RowExclusive lock on the same table。但是,獨佔鎖與共享衝突,所以事務2正在等待事務1的事務ID。 docs提到事務鎖是一種等待其他事務的方式。因此,它看起來像事務2,儘管已經提交,仍然在等待事務1。

    當事務1繼續時,它想要獲取事務2上的共享鎖,並且這會造成死鎖。爲什麼它想要獲得交易2上的共享鎖?不太確定。 The docs提示該信息在pg_locks中不可用。我會猜測這與MVCC有關,但對我來說仍然是個謎。

  • +1

    這是由於外鍵鎖不是索引鎖,請看Denis的回答是否正確。 – RichVel

    +0

    @RichVel感謝您提出這一點。我進一步研究了它。看起來我們都錯了。我很困惑這些交易鎖是如何工作的。 –

    +1

    真正困惑的問題多於提問而不是回答問題。 [另外,「Postgre」對於Postgres來說是明確不正確的。](http://wiki.postgresql.org/wiki/Identity_Guidelines) –