我需要完成以下任务:
我需要在同一个表中插入两行,要么两者都插入,要么都不插入(原子插入两行)。
我使用 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 的现有行吗?这是没有种族条件的吗?
您可以使用信号灯类型的体系结构。例如,创建一个名为"信号量"的表或任何您想要调用它的表。假设表中只有一个字段,并且该字段是唯一的,称为"sem"。现在,运行"插入信号量集 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;
之后删除的原因有两个:
- 我想你不需要删除它,但让我们去清理数据,我们;)
- 在 COMMIT 之后删除的原因是您希望在事务完成之前保留阻塞锁。
旁注:信号量通常在自动增量字段不起作用时使用。然后,使用仅包含一条记录的信号量表来序列化插入并阻止主键。
希望对:)有所帮助