【问题标题】:Concurrent getting tasks from MySQL table从 MySQL 表并发获取任务
【发布时间】:2017-01-04 15:42:48
【问题描述】:

我有很多执行不同任务的控制台应用程序。我从 php 脚本中获得了独特的任务:

$mysqli->autocommit(FALSE);
$result = $mysqli->query("SELECT id, task FROM queue WHERE locked = 0 LIMIT 1 FOR UPDATE;");

while($row = $result->fetch_assoc()){ 
    $mysqli->query('UPDATE queue SET locked = 1 WHERE id="'.$row['id'].'";');
    $mysqli->commit();
    $response["response"]["task"] = $row["task"];
}

$mysqli->close();   
echo json_encode($response);

有时我有重复的任务,并且“尝试获取锁定时发现死锁;尝试重新启动事务”。 我做错了什么?

UPD:在“锁定”列上设置索引解决问题

【问题讨论】:

  • 不认为和错误有关,但是为什么在指定LIMIT 1时使用while循环呢?它永远不能返回超过一行,所以你不需要循环。
  • locked 列上有索引吗?尝试添加它。
  • 您的应用程序应根据dev.mysql.com/doc/refman/5.7/en/innodb-deadlocks-handling.html处理此类错误,然后重新提交,以防出现错误。设置索引并保持较小的事务可以最大限度地降低死锁风险。
  • 这段代码没有意义!您设置为运行事务,然后在每次更新后提交?你选择使用LIMIT 1,然后while循环一个结果??
  • 我在这里看到了数百个问题,人们使用while 来处理查询结果,即使它只能返回 1 行,仅仅是因为这是他们习惯的模式。新手程序员不根据逻辑进行设计,他们只是复制他们以前看到或写过的东西而不考虑它。

标签: php mysql transactions deadlock


【解决方案1】:

来自 MySQL 文档How to Minimize and Handle Deadlocks

为您的表添加精心挑选的索引。然后您的查询需要扫描更少的索引记录并因此设置更少的锁。

locked 列添加索引应该可以解决这个问题。没有它,SELECT 查询必须扫描整个表,寻找带有locked = 0 的行,并且它所遍历的所有行都必须被锁定。

如果您在WHERE 子句中为列添加索引,它可以直接转到该记录并锁定它。没有其他记录被锁定,因此减少了死锁的可能性。

【讨论】:

    猜你喜欢
    • 2013-12-06
    • 2018-04-15
    • 2018-02-12
    • 2021-12-04
    • 2017-04-03
    • 2021-03-28
    • 1970-01-01
    • 1970-01-01
    • 2020-01-21
    相关资源
    最近更新 更多