InnoDB选择更新

时间:2017-04-20 21:18:00

标签: mysql pdo innodb

如果我对InnoDB表有一个简单的陈述

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

是否需要真正选择SELECT FOR UPDATE?

由于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();

3 个答案:

答案 0 :(得分:2)

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

对于给定事务中的所有资源,首先

SELECT FOR UPDATE可以防止或缩短死锁情况。

假设以下情形:

  • 处理A更新表1&amp; 2顺序1,2
  • 流程B更新表1&amp; 2按顺序2,1

现在处理A更新,表1,同时进程B更新表2,导致死锁(假设相同的&#34;记录/页面&#34;被此更新命中)。

但是,如果在事务开始时使用SELECT FOR UPDATE,则事务将在开始时失败,因为无法锁定表2(或者更快的1个)。这里的关键部分是&#34;在交易开始时#34;如果你以后再这样做,那么只运行UPDATE同样有效。

密钥始终是保持事务的原子性和快速性:对SQL逻辑进行分组,使其能够以最少的其他代码执行,保持锁定时间尽可能短。

答案 1 :(得分:0)

如果你需要做的只是

BEGIN;
UPDATE ...
COMMIT;

然后只做那个。

如果您需要这样做:

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

然后您执行需要FOR UPDATE以防止其他连接更改T.

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

答案 2 :(得分:0)

UPDATE...WHERE 语句自动获取正在更新的行上的排他锁。您无需显式启动事务或使用 SELECT..FOR UDPATE

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

如果有数千个客户端,每个客户端都必须获得排他锁,因此在他们进行自己的更改之前,他们可能会被另一个首先获得它的客户端阻止。