我需要完成以下任务:
我需要在同一个表中插入两行,两者都插入,或者两者都不插入(原子插入两行)。
我使用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 :(得分:4)
您可以使用信号量类型的架构。例如,创建一个名为“信号量”的表或任何您想要调用它的表。假设表中只有一个字段,该字段是唯一的,称为“sem”。现在,运行“INSERT INTO信号量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;
之后进行删除的原因有两个:
旁注:通常在自动增量字段不起作用时使用信号量。然后使用一个只有一条记录的信号量表来序列化插入并阻止主键。
希望有所帮助:)