2009-06-01 83 views
3

我在使用MySQL和PHP + Propel 1.3時似乎遇到併發問題。下面是Propel對象的「保存」方法的一個小例子。併發問題

public function save(PropelPDO $con = null) { 
    $con = Propel::getConnection(); 
    try { 
     $con->beginTransaction(); 
     sleep(3); // ignore this, used for testing only 
     parent::save($con); 
     $foo = $this->getFoo(); // Propel object, triggers a SELECT 

     // stuff is happening here... 

     $foo->save($con); 
     $con->commit(); 
    } catch (Exception $e) { 
     $con->rollBack(); 
     throw $e; 
    } 
} 

問題是$ foo對象。假設我們在很短的時間內陸續接到兩個示例方法的調用。在某些情況下,如果第二個事務讀取$ foo的...

$foo = $this->getFoo(); 

...第一個事務不得不保存它的機會之前...

$foo->save($con); 

... $ FOO通過第二筆交易閱讀將會過時,並且會發生不好的事情。

如何強制鎖定表Foo對象存儲在後,只有第一個完成其工作後,後續事務才能從它讀取?

編輯:上下文是一個web應用程序。總之,在某些情況下,我想要第一個請求做一些數據修改(發生在$ foo的獲取和保存之間)。所有後續請求都不應該能夠進行修改。該修改是否會發生取決於獲取的$ foo狀態(表格行屬性)。如果兩個事務獲取相同的$ foo,則修改將發生兩次,這會導致問題。

+0

你是否在循環中調用這個保存方法?或者你是否在談論這是跨連接的問題,因爲兩個用戶幾乎在同一時間點擊此資源? – 2009-06-01 18:29:37

+0

後一種情況。 – Ree 2009-06-01 18:34:52

+0

如果這是一個基於瀏覽器的應用程序,它們不必在同一時間。以此示例爲例:人A和B在某個時間點加載相同的數據行。人A進行更改並保存,屏幕重新加載,數據看起來不錯。 C人員加載屏幕,確認人員A的變更。人B最終保存數據。人A加載數據,但他們的改變現在不見了。 – 2009-06-01 18:51:33

回答

1

當您將此現有行加載到屏幕/應用程序時,也加載LastChgDate。當您保存它時,請使用「AND LastChgDate = 」。檢查更新的受影響行數,如果它爲零,則返回「別人已經保存此記錄」的錯誤,並回滾和其他更改。有了這個邏輯,你只能保存一行,如果它與加載時相同。對於新行INSERT,這不是必須的,因爲它們是新的。

0

在MySQL中,我認爲你可以使用SELECT FOR UPDATE來完成鎖定。

另一種選擇是使用GET_LOCK和RELEASE_LOCK MySQL函數調用來創建用來控制對資源的訪問的命名鎖。

這些方法有一些缺點。我自己沒有用過它們,它們都是MySQL專用的,但它們可以爲你工作。