2014-02-24 36 views
0

在PHP中,使用內置的PDO數據庫抽象層,它的情況並不少見有這樣的代碼:PDO嵌套事務引起PHP掛起

function B() 
{ 
    $db = new PDO(...); 
    $db->beginTransaction(); 

    // do something 

    $db->commit(); 
} 

function A() 
{ 
    $db = new PDO(...); 
    $db->beginTransaction(); 

    // do something 

    B(); 

    $db->commit(); 
} 

注意,A和B都獲得自己的數據庫連接,並且都開始交易,並且A呼叫B。結果是,在某些情況下(即,B在做某事,其中取決於A確定了),則該編碼模式將導致死鎖。

例如,假設我們有表格FooBarFoo包含引用Bar的外鍵,並設置爲ON DELETE CASCADE。假設函數A更新Foo以更改該外鍵字段的值,然後函數B刪除Bar中的現在未使用的記錄。因爲A的事務未提交,所以B中的SQL將等待它,儘管在B返回之前它將永不會提交。 有沒有辦法解決這個問題,而不將數據庫對象傳遞給每個函數?它看起來像PDO本身應該能夠正確處理這一點,只需跟蹤嵌套事務。

我曾嘗試在評論here,其中PDO是子類做嵌套事務計數的解決方案,但它似乎並沒有實際工作 - 我認爲是因爲A()B()$db對象是不一樣的情況下,所以他們不知道彼此,並且在任一情況下計數器的值都是不正確的。

+2

「這並不罕見」?真? –

+0

當然,爲什麼不呢?這似乎是最明顯的實現。例如,[PHP文檔](http://www.php.net/manual/en/pdo.connections.php)似乎就是這樣做的 - 參見「Example#2」。你會建議什麼? – CmdrMoozy

回答

2

您應該只使用PDO的一個實例。你需要弄清楚如何傳遞它。有很多方法。直接傳遞,單身,全局函數。選擇一種方法並使用它。

+0

在我看來,這似乎是PDO連接池代碼應該處理的事情。此外,我沒有看到文檔中的任何地方,它說,一次只能存在一次實例。你有一些參考,這是設計事物的正確方法嗎? – CmdrMoozy

0

如果你真的打算讓A()和B()分別在他們自己的交易中,那麼這在任何級別都是無法解決的。你只是編碼一個保證的死鎖。

如果你不在乎B()是否在自己的交易中,只要它在一些交易,那麼你仍然有一個不可能的情況。 B()不能在A()的事務中,因爲事務不能跨越連接。但是B()不能在不引起死鎖問題的獨立事務中。因此,B()不能在任何事務中。

如果不關心B()是否在事務中,至少在這種情況下,那麼您需要某種關於事務嵌套的應用程序級內省。 B()必須檢查並遵守這一點。但是PDO不能做到這一點,除非你把它包含到至少包含一些單例功能。但我想你想在事務中的B()。你只是希望它在A()的事務中,這樣就沒有死鎖。

在這種情況下,唯一好的解決方案是Damien's,儘管您仍然需要使用跟蹤嵌套事務的PDO包裝器之一。這是讓您的函數在獨立調用時啓動事務的唯一方法,但要知道只有第一個函數在以嵌套方式調用時纔會啓動事務。