InnoDB Select 更新

InnoDB Select For Update

如果我有一个 InnoDB 的简单语句 Table

UPDATE <table> SET locked=1, col2=<Val2> WHERE locked=0

真的需要SELECT更新吗?

既然 InnoDB 支持行级锁定,即使成千上万的客户端同时执行同一个脚本,是否有可能发生冲突?

更新:

像这样可以防止锁定

$dbh = new PDO(DSN, DB_USER, DB_PASS);
$dbh->beginTransaction();
$selQuery = $dbh->prepare("SELECT <col> FROM <table> WHERE status=0 LIMIT 1 FOR UPDATE");
$selQuery->bindColumn(<col1>, $col1);
$selQuery->execute();

$selQuery->fetch(PDO::FETCH_BOUND);

$dbh->query("UPDATE <table> SET status=1 WHERE status=0 LIMIT 1");

$dbh->commit();

是的,碰撞总是会发生。所以使用设计模式:

SELECT FOR UPDATE 在给定的事务中首先获取所有资源,可以防止或缩短死锁情况。

假设以下场景:

  • 进程 A 按 1,2
  • 的顺序更新 table 1 & 2
  • 进程 B 按照 2,1
  • 的顺序更新 table 1 & 2

现在进程A更新,table1,进程B同时更新table2,造成死锁(假设是命中了同一个“records/pages”通过这次更新)。

但是,如果 SELECT FOR UPDATE 将在事务开始时使用,事务将在开始时阻塞,因为 table 2(或 1,以更快者为准)不能被锁定(还).这里的关键部分是“在交易开始时”,如果您稍后再做,那么 运行 和 UPDATE 一样有效。

关键始终是保持事务的原子性和快速性:对 SQL 逻辑进行分组,以便它可以在中间执行最少数量的其他代码,并尽可能缩短锁定时间。

如果您只需要

BEGIN;
UPDATE ...
COMMIT;

那就只做那个。

如果您需要这样做:

BEGIN;
SELECT stuff from table T;  -- needs FOR UPDATE
work with the stuff, and eventually 
UPDATE T ...;
COMMIT;

然后你需要FOR UPDATE来防止另一个连接改变T。

如果没有 FOR UPDATE 如果在您处理这些内容时更改了 T,那么您要么逐步执行他们所做的更改。或者他们可以更改使您的 UPDATE 不正确的内容。

UPDATE...WHERE 语句自动获取正在更新的行的独占锁。您不需要显式启动事务或使用 SELECT..FOR UDPATE.

如果您选择行然后稍后更新它们,那么这就是使用 SELECT..FOR UPDATE 的地方。这可以防止任何其他客户端在您更新行之前看到这些行的旧数据。

如果有数千个客户端,每个客户端都必须获得独占锁,因此它们可能会被另一个先获得它的客户端阻止,然后才能进行自己的更改。