2012-06-15 40 views
3

我有一個應用程序,在PHP + MYSQL plattform運行,使用Doctrine2框架。我需要在一個http請求期間執行3個db查詢:第一個INSERT,第二個SELECT,第三個UPDATE。 UPDATE依賴於SELECT查詢的結果。併發http請求的概率很高。如果出現這種情況,並且DB查詢混淆了(例如,INS1,INS2,SEL1,SEL2,UPD1,UPD2),則會導致數據不一致。我如何確保INS-SEL-UPD操作的原子性?我是否需要使用某種鎖,或者交易是否足夠?併發學說

+0

您打算從一張表或一張表讀取數據嗎? – RandomSeed

+0

只寫入一張表 – puty

回答

2

一個表範圍LOCK保證在所有情況下工作。但是它們很糟糕,因爲它們會阻止併發,而不是處理它。 但是,如果您的腳本在短時間內持有鎖的密碼,它可能是一個可接受的解決方案。

如果你的表使用InnoDB引擎(用於交易的MyISAM不支持),交易是最有效的解決方案,同時也是最複雜的。

的非常具體的需要(在同一個表,先INSERT,第二選擇,第三次更新dependending在SELECT查詢的結果):

  1. 開始交易
  2. 插入您的記錄。其他交易將不會看到這些新行,直到自己的事務被提交(除非你使用的是非標準isolation level
  3. SELECT...LOCK IN SHARE MODE選擇您的結果。您現在對這些行具有READ鎖定,其他人可以更改這些行。 (*)
  4. 計算您需要計算的任何內容,以確定是否需要更新某些內容。
  5. 如果需要更新行。
  6. 提交
  7. 隨時期待錯誤。如果檢測到死鎖,MySQL可能會決定ROLLBACK您的事務以避免死鎖。如果另一個事務正在更新您嘗試讀取的行,您的事務可能會被鎖定一段時間,甚至超時。

,如果你繼續這樣,您的事務的原子有保證。

一般

(*),行由該SELECT可能仍然在併發事務被插入,即,不存在未在整個交易過程中保證除非proper precautions採取返回

+0

如果您進行交易,問題太大而無法在一般情況下解決。我最好的建議是:閱讀大量關於併發性的知識,瞭解MySQL鎖定/事務如何工作。你可能想詳細描述你的程序的邏輯(你插入了什麼,在哪種情況下你會更新哪一行)。 – RandomSeed

+0

@副:很多編輯。首先,我誤解了你最初的問題。我沒有看到你的問題其實很具體。 – RandomSeed

8

@YaK的答案實際上是一個很好的答案。你應該知道如何處理一般的鎖。

尋址Doctrine2具體而言,你的代碼應該是這樣的:

$em->getConnection()->beginTransaction(); 
try { 
    $toUpdate = $em->find('Entity\WhichWillBeUpdated', $id, \Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE); 
    // this will append FOR UPDATE http://docs.doctrine-project.org/en/2.0.x/reference/transactions-and-concurrency.html 
    $em->persist($anInsertedOne); 
    // you can flush here as well, to obtain the ID after insert if needed 
    $toUpdate->changeValue('new value'); 
    $em->persist($toUpdate); 
    $em->flush(); 
    $em->getConnection()->commit(); 
} catch (\Exception $e) { 
    $em->getConnection()->rollback(); 
    throw $e; 
} 

的每個後續請求的更新來獲取,會等到其獲取鎖定一個過程本次交易完成。事務成功完成或失敗後,Mysql會自動釋放鎖。缺省情況下,innodb鎖定超時時間爲50秒。所以如果你的進程在50秒內沒有完成事務處理,它將自動回滾並釋放鎖定。您的實體不需要任何其他字段。