2012-08-10 71 views
4

條件我需要完成以下任務:鎖SQL表,則執行交易,用於插入上選擇值

我需要兩行插入到相同的表,並且或者兩個被插入,或兩者都不是(原子插入兩排)。

我做這在一個InnoDB表使用事務,像這樣:

$db->beginTransaction(); 

# Using PDO prepared statments, execute the following queries: 
INSERT INTO t1 set uid=42, foo=1 
INSERT INTO t1 set uid=42, foo=2 

$db->commit(); 

不過我也只希望如果有在列具有值「42」表中沒有任何行插入這些行。

所以我做的:

$stmt = $db->prepare("SELECT id FROM t1 WHERE uid != ?"); 
$stmt->execute(array(42)); 
if($stmt->fetchColumn() < 0){ 
    # There is no row with uid=42, so 
    # perform insertions here as per above. 
    INSERT INTO t1 set uid=42, foo=1 
    INSERT INTO t1 set uid=42, foo=2 
} 

但是這裏有一個競爭條件,因爲與UID = 42檢查該行之後,並插入新行權前可以插入一排。

我應該如何解決這個問題?

我可以鎖定表,然後在表鎖中執行InnoDB事務嗎?

我可以在事務內部做選擇以檢查uid = 42的現有行嗎?那種族沒有條件嗎?

+0

看看REPLACE INTO ..而不是INSERT。但是,如果記錄已經存在,它將執行更新。 – Waygood 2012-08-10 16:30:44

+2

看到你的表格模式會很有趣。這聽起來像你想要做的是強制一個唯一的索引,而沒有一個唯一的索引字段本身。雖然我可以看到你試圖用同一個uid插入多行,這對我來說並不合適。看起來你的問題可能通過表格標準化和使用唯一索引來解決。 – 2012-08-10 16:31:39

+0

我的想法是重複檢查42 - 在你提交之前和之後。 – 2012-08-10 16:32:32

回答

4

您可以使用信號量類型的體系結構。例如,創建一個名爲「信號燈」的表格或任何你想調用它的表格。假設表中只有一個字段,並且該字段是唯一的,稱爲「sem」。現在,運行「INSERT INTO semaphore SET sem = 42」;

好吧,那個INSERT語句是原子的,並且意味着在其他人嘗試插入42之後的那一刻,他們會得到一個錯誤,指出重複鍵。

然後,在原始表格中執行插入操作。在交易中完成所有這些。在SQL它應該是這樣的:

BEGIN TRANSACTION; 
INSERT INTO semaphore SET sem = 42; 
INSERT INTO t1 set uid=42, foo=1; 
INSERT INTO t1 set uid=42, foo=2; 
COMMIT; 
DELETE FROM semaphore WHERE sem = 42; 

你在刪除的原因後來有兩方面:

  1. 我猜你並不需要將其刪除,但讓我們去清理數據應我們;)
  2. 您在COMMIT之後刪除的原因是您希望在事務完成之前保留對其的鎖定鎖定。

旁註:信號量通常用於自動遞增字段不會執行的情況。然後使用只有一條記錄的信號量表來序列化插入並阻止主鍵。

希望它有幫助:)

+0

感謝您的回答。我願意這樣做,儘管你會認爲SQL/InnoDB有一個功能來處理這個問題,而不需要一個新的信號量表。 – Olhovsky 2012-08-10 17:55:18

+0

如果您需要將所有交易數據保存在一張表中,肯定是一個可行的解決方案。 – 2012-08-10 18:03:18

+2

您可以使用應用程序級別鎖而不是信號量表 - 請參閱[GET_LOCK](http://dev.mysql.com/doc/refman/5.0/zh-cn/miscellaneous-functions.html#function_getlock) – 2012-08-13 16:18:07