2015-04-06 96 views
0

假設我有以下情況:一個用戶只能有一個活動任務。MySQL競爭條件 - 防止多次插入

所以,如果我想添加任務ID爲2的用戶,我需要做的:

SELECT * FROM tasks WHERE active=1 AND id_user = 2; 

,如果有沒有記錄,我可以做

INSERT INTO tasks(id_user, active) VALUES(2,1); 

的問題是:如果我把選擇和插入事務(並使用InnoDB)我可以肯定,用戶不會創建2個活動任務?

上述情況簡化了,但我被告知,在MySQL備份期間(mysql插入以某種方式由服務器排隊),用戶創建了許多不允許使用這樣的代碼的活動任務(目前我還不知道事務被使用)。根據「故事」的唯一工作解決方案是在完成另一個隊列後使用隊列(可能是Beanstalkd)來完成運行任務。

所以問題是 - 是否有可能發生這樣的事情,如果使用MySQL事務將工作的解決方案或應該使用其他解決方案在這種情況下?

編輯

這裏的問題是我不能爲列添加獨特的鍵,我也不能更新記錄。這只是爲了更準確,真實的情況是:

在表中的任務有:

ID - 主鍵,自動遞增 id_user - 可空(設置爲null,如果 - 用戶 起始日期 END_DATE ID任務未完成)

一個用戶只能有一個任務未完成(end_date爲空),因此當至少有一個任務的用戶空日期時,無法創建該用戶的新任務。不過我老數據庫,在那裏它很可能有可能將我需要先檢查之前每次是相同的使用,如果END_DATE設置爲null多條記錄,所以我不能在這裏使用獨特的經營

SELECT * FROM tasks WHERE end_date IS NOT null AND id_user = 2 

如果沒有記錄,那麼我可以爲用戶創建新的任務。

在將來創建新任務之前,還可能會增加更多的此選擇條件來驗證。

回答

0

瞭解InnoDB「交易」。瞭解SELECT ... FOR UPDATE

但是,針對這種情況,既可以通過做可以避免:

UPDATE tasks SET active = 1 WHERE id_user = 2; -- not adequate 

然後,檢查的RowsAffected:0意味着它並不活躍。

隨着編輯,劃傷我的UPDATE;回到交易。在僞代碼中:

BEGIN; 
$active = SELECT active FROM tasks WHERE id_user = 2 FOR UPDATE; 
if ($active) exit; 
INSERT INTO tasks (id_user, active, task) 
    VALUES (2, 1, 'foo') 
    ON DUPLICATE KEY UPDATE active = 1 task = 'foo'; 
COMMIT; 

這假設你有PRIMARY KEY(id_user)

0

如果你有一個UNIQUE指標,防止重複,這樣做:

INSERT IGNORE INTO tasks (id_user, active) VALUES (2,1) 

這將忽略有關重複條目的警告。這具有隻添加記錄一次的效果。

+0

添加'UNIQUE'列是實現這一目標的方法。沒有這一點,你將不得不寫一些醜陋,令人費解的過程,並將其包裝在交易中。 – tadman