【问题标题】:How can I use transactions in MySQL to avoid "lost updates"?如何在 MySQL 中使用事务来避免“丢失更新”?
【发布时间】:2017-09-20 06:57:50
【问题描述】:

当两个不同的事务试图 同时更新数据库中同一行上的同一列 时间。通常,一个事务会更新一个特定列 特定的行,而另一行很快就开始了 在更新相同的值本身之前看不到此更新。结果 然后第一笔交易的“丢失”,因为它只是被覆盖 通过第二次交易。 --https://morpheusdata.com/blog/2015-02-21-lost-update-db

【问题讨论】:

    标签: mysql sql concurrency transactions


    【解决方案1】:

    你有两种可能

    1. 在悲观锁定的情况下,如果您打算更新刚刚读取的数据,请选择更新。然后只有一个可以读取记录,直到当前事务完成,其他尝试选择更新的人必须等待。
    2. 在乐观锁定的情况下,您可以制定更新语句,以便在选择和更新之间发生变化的情况下,不会发生更新。在您的情况下,您可以使用:

      UPDATE product set quantity = 10 
             where id = 1 and quantity = <original quantity -- 7>
      

      如果不是预期的记录数,通常是1,已经被更新,因为同时另一个进程已经完成了数量的更新,那么你必须在更新之前重复选择。 你怎么知道,有多少条记录被更新了?这取决于您用于执行数据库请求的技术,但根据我的经验,每个 Sql-Dbms 都会将该信息返回给其客户端。

    【讨论】:

      【解决方案2】:

      这也称为“竞争条件”。您的问题已经有了答案:您“使用事务”,您是否工作,然后COMMIT 每个线程中的事务。现在是细节:

      • 如果类型 InnoDB,您的表必须是
      • 默认情况下,MySQL 连接每个命令使用 1 个事务,基本上在每次写入后自动提交数据。您需要 START TRANSACTION 或禁用自动提交:例如 PHP 中的 $mysqli-&gt;autocommit(FALSE);
      • 你需要注意你的操作结果和ROLLBACK错误并停止你正在做的事情
      • 您确实必须记住在完成更改后COMMIT,否则系统会认为有错误并为您ROLLBACK

      【讨论】:

        【解决方案3】:

        要么

        设置事务隔离级别可重复读取

        -- 并遭受一些死锁和一些性能损失
        -- 没有看起来那么糟糕,但取决于您的需求
        -- REPEATABLE READ 是 InnoDB 的默认级别

        选择更新

        --好吧,你写了代码,你知道哪些选择需要锁定其他人
        -- 这假设你的隔离级别是 READ_COMMITTED

        有关隔离级别的更多信息可以在 MySql 文档中找到(这次简短而清晰) https://dev.mysql.com/doc/refman/5.5/en/innodb-transaction-isolation-levels.html

        【讨论】:

        • 据我所知,MySQL 中的可重复读取隔离级别并不能防止丢失更新。所以你的第一个选择是行不通的。
        猜你喜欢
        • 2012-01-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-07-30
        • 1970-01-01
        • 2022-06-13
        • 1970-01-01
        相关资源
        最近更新 更多