2015-04-06 62 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