2013-03-08 143 views
1

我的理解是,Apache爲每個傳入請求創建一個單獨的PHP進程。這意味着,如果我有代碼,不會是這樣的:如果記錄存在 Apache,MySQL和PHP競爭條件

  • 如果記錄不存在,創建它
  • 然後,這是容易受到競爭條件

    1. 檢查,是它不是?如果兩個請求同時進入,並且它們同時觸發(1),它們都會返回錯誤,然後都會嘗試插入新記錄。

      如果是這樣,人們如何處理這個問題?圍繞這2個請求創建一個MySQL事務會解決這個問題,還是我們需要做一個完整的表鎖?

    +0

    INSERT IGNORE,LOCK TABLE或在不想複製的列上使用UNIQUE索引都是很好的選擇。 – 2013-03-08 01:08:34

    +0

    另請參閱http://stackoverflow.com/questions/1361340/how-to-insert-if-not-exists-in-mysql – 2013-03-08 01:10:17

    +0

    'SELECT ...FOR UPDATE' – 2013-03-08 01:23:52

    回答

    2

    據我所知,您不能通過不同的連接創建一個事務。也許一個解決方案是設置你正在檢查的列是唯一的。這樣,如果兩個連接都是10,並且10不存在。他們都會嘗試創建10。一會先完成插入行,一切都很好;那麼後面的連接會失敗,因爲該列不是唯一的。如果發現拋出異常,則可以從數據庫中隨後記錄SELECT

    0

    我認爲在第二步處理錯誤應該是足夠的。如果兩個進程嘗試創建一條記錄,那麼只要你已經適當地配置了MySQL表格,其中一個就會失敗。在正確的字段中使用UNIQUE是實現這一訣竅的一種方法。

    0

    Apache不會「爲每個傳入請求創建單獨的PHP進程」。它使用一組進程(默認,prefork模式)或線程。

    正如您所提到的,競爭條件也可能被引用(或導致)數據庫「死鎖」。 @ see what is deadlock in a database?

    在需要的地方使用事務應該解決這個問題,是的。
    通過確保您檢查記錄是否存在並在事務中創建它,整個操作是原子操作。
    因此,其他請求不會嘗試創建重複記錄(或者,根據實際查詢,創建不一致或輸入實際的死鎖)。

    另請注意,MySQL尚未支持嵌套事務: 您不能在事務內部擁有事務,因爲第一次提交將提交所有內容。

    +0

    +1對於線程的區別 – landons 2013-03-08 01:31:04

    +3

    這是一個簡單的競態條件,並且與死鎖沒有任何關係 – 2013-03-08 01:38:22

    +3

    使用事務(單獨)不會解決問題,因爲兩個事務都可以看到該記錄還不存在。 – 2013-03-08 01:47:50

    1

    老實說,我很少遇到這種情況。通常可以通過重新評估業務需求來緩解這種情況。即使兩個不同的用戶試圖插入完全相同的數據,我也會推遲重複管理用戶,而不是應用程序。但是,如果有理由在應用程序邏輯中強制執行唯一約束,那麼我會使用INSERT IGNORE... ON DUPLICATE KEY UPDATE...查詢(當然在表中具有相應的UNIQUE索引)。