如果我对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();
答案 0 :(得分:2)
是的,总是会发生碰撞。所以使用设计模式:
对于给定事务中的所有资源,首先 SELECT FOR UPDATE
可以防止或缩短死锁情况。
假设以下情形:
现在处理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
的地方。这可以防止任何其他客户端在更新行之前看到这些行的旧数据。
如果有数千个客户端,每个客户端都必须获得排他锁,因此在他们进行自己的更改之前,他们可能会被另一个首先获得它的客户端阻止。