【发布时间】:2021-10-07 02:35:45
【问题描述】:
我有一个 PHP 脚本 (deleteAndReInsert.php),它会删除 name = 'Bob' 所在的所有行,然后使用 name = 'Bob' 插入 1000 个新行。这可以正常工作,最初为空的表最终有 1000 行,如预期的那样。
$query = $pdo->prepare("DELETE FROM table WHERE name=?");
$query->execute(['Bob']);
$query = $pdo->prepare("INSERT INTO table (name, age) VALUES (?,?)");
for ($i = 0; $i < 1000; $i++)
{
$query->execute([ 'name' => 'Bob', 'age' => 34 ]);
}
问题是如果我运行deleteAndReInsert.php 两次(几乎在同一时间),最终表包含超过 1000 行。
似乎正在发生的事情是第一次运行的 DELETE 查询完成,然后许多(但不是全部)1000 INSERTS 被调用。
然后第二个 DELETE 查询开始并在前 1000 个 INSERTS 完成之前完成(比如 1000 个 INSERTS 中的 350 个完成)。现在第二个 1000 INSERTS 运行,我们最终得到 1650 行而不是 1000 总行,因为在第二个 DELETE 被调用后仍有 1000 - 350 = 650 INSERTS 剩余。
防止此问题发生的正确方法是什么?我应该将所有内容包装在事务中,还是应该进行 1 次批量插入调用而不是 1000 次单独插入?显然我可以实现这两种解决方案,但我很好奇哪一种可以保证防止这个问题。
【问题讨论】:
-
一个事务可能对你没有帮助,因为你有多个插入,并且没有真正的方法来判断哪个是对还是错。相反,我会研究应用程序级别的锁,这可以手动完成,也可以通过symfony 等组件完成。当您的脚本启动时,它应该在完成之前将自己锁定以防止其他运行。
-
您可以在写入之前锁定整个表...事务无济于事,因为这只会隔离操作,但不会阻止其他人访问表。
-
桌子上的
PRIMARY KEY是什么?为完整起见,请提供SHOW CREATE TABLE table。 -
有什么问题??肯定1650计数错了,但表没有错。你最终得到正好 1000 行。您没有提到是否涉及第二个连接。如果这是问题的一部分,您必须说明它是如何涉及的。
-
@RickJames 没有
PRIMARY KEY,还有第二个连接。我几乎同时在两个不同的浏览器窗口中打开 deleteAndReInsert.php。
标签: php mysql sql locking race-condition