我的理解是,Apache爲每個傳入請求創建一個單獨的PHP進程。這意味着,如果我有代碼,不會是這樣的:如果記錄存在 Apache,MySQL和PHP競爭條件
然後,這是容易受到競爭條件
- 檢查,是它不是?如果兩個請求同時進入,並且它們同時觸發(1),它們都會返回錯誤,然後都會嘗試插入新記錄。
如果是這樣,人們如何處理這個問題?圍繞這2個請求創建一個MySQL事務會解決這個問題,還是我們需要做一個完整的表鎖?
我的理解是,Apache爲每個傳入請求創建一個單獨的PHP進程。這意味着,如果我有代碼,不會是這樣的:如果記錄存在 Apache,MySQL和PHP競爭條件
然後,這是容易受到競爭條件
如果是這樣,人們如何處理這個問題?圍繞這2個請求創建一個MySQL事務會解決這個問題,還是我們需要做一個完整的表鎖?
據我所知,您不能通過不同的連接創建一個事務。也許一個解決方案是設置你正在檢查的列是唯一的。這樣,如果兩個連接都是10
,並且10
不存在。他們都會嘗試創建10
。一會先完成插入行,一切都很好;那麼後面的連接會失敗,因爲該列不是唯一的。如果發現拋出異常,則可以從數據庫中隨後記錄SELECT
。
我認爲在第二步處理錯誤應該是足夠的。如果兩個進程嘗試創建一條記錄,那麼只要你已經適當地配置了MySQL表格,其中一個就會失敗。在正確的字段中使用UNIQUE是實現這一訣竅的一種方法。
Apache不會「爲每個傳入請求創建單獨的PHP進程」。它使用一組進程(默認,prefork模式)或線程。
正如您所提到的,競爭條件也可能被引用(或導致)數據庫「死鎖」。 @ see what is deadlock in a database?
在需要的地方使用事務應該解決這個問題,是的。
通過確保您檢查記錄是否存在並在事務中創建它,整個操作是原子操作。
因此,其他請求不會嘗試創建重複記錄(或者,根據實際查詢,創建不一致或輸入實際的死鎖)。
另請注意,MySQL尚未支持嵌套事務: 您不能在事務內部擁有事務,因爲第一次提交將提交所有內容。
+1對於線程的區別 – landons 2013-03-08 01:31:04
這是一個簡單的競態條件,並且與死鎖沒有任何關係 – 2013-03-08 01:38:22
使用事務(單獨)不會解決問題,因爲兩個事務都可以看到該記錄還不存在。 – 2013-03-08 01:47:50
老實說,我很少遇到這種情況。通常可以通過重新評估業務需求來緩解這種情況。即使兩個不同的用戶試圖插入完全相同的數據,我也會推遲重複管理用戶,而不是應用程序。但是,如果有理由在應用程序邏輯中強制執行唯一約束,那麼我會使用INSERT IGNORE... ON DUPLICATE KEY UPDATE...
查詢(當然在表中具有相應的UNIQUE索引)。
INSERT IGNORE,LOCK TABLE或在不想複製的列上使用UNIQUE索引都是很好的選擇。 – 2013-03-08 01:08:34
另請參閱http://stackoverflow.com/questions/1361340/how-to-insert-if-not-exists-in-mysql – 2013-03-08 01:10:17
'SELECT ...FOR UPDATE' – 2013-03-08 01:23:52