【问题标题】:InnoDB Select For UpdateInnoDB 选择更新
【发布时间】:2017-04-20 21:17:59
【问题描述】:

如果我有一个针对 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();

【问题讨论】:

    标签: mysql pdo innodb


    【解决方案1】:

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

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

    假设以下场景:

    • 进程 A 按 1,2 的顺序更新表 1 和 2
    • 进程 B 按 2,1 的顺序更新表 1 和 2

    现在进程 A 更新表 1,进程 B 同时更新表 2,导致死锁(假设此更新命中相同的“记录/页面”)。

    如果在事务开始时使用SELECT FOR UPDATE,则事务将在开始时阻塞,因为表 2(或更快的表 1)无法锁定(还)。这里的关键部分是“在事务开始时”,如果你稍后再做,那么只运行UPDATE 就同样有效。

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

    【讨论】:

    • 我用应该可以工作的代码更新了这个问题。谢谢!
    • 事务不会失败。 SELECT...FOR UDPATE 在行上创建一个排他锁。进程 B 将被进程 A(或哪个更快)阻塞,直到释放独占锁。也只是为了明确 SELECT...FOR UDPATE 执行行级锁定,而不是表级锁定。由于使用了行级锁,仍然有可能发生死锁,具体取决于事务中的执行顺序以及是否有多个非原子选择。 dev.mysql.com/doc/refman/8.0/en/innodb-locking-reads.html
    • @menzoic 更新了答案。谢谢:)
    【解决方案2】:

    如果你需要做的就是

    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 不正确的内容。

    【讨论】:

      【解决方案3】:

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

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

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

      【讨论】:

        猜你喜欢
        • 2014-03-30
        • 1970-01-01
        • 2013-01-28
        • 2017-10-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-10-01
        相关资源
        最近更新 更多