【问题标题】:Intermittently getting old data from mysql innodb queries间歇性地从 mysql innodb 查询中获取旧数据
【发布时间】:2015-04-03 05:35:37
【问题描述】:

我们有各种表格来表示各种类型的数据。每个表都有一个相应的修订表来跟踪此数据的历史记录。每个修订(修订表中的条目)都有一个唯一编号。此编号存储在更改元数据表中。这些表中的每一个都引用了一个 parent_id。在对表进行任何更改之前,我们使用 SELECT ... FOR UPDATE 锁定父行。

在进行更新/插入后,我们还会增加更改编号并将该编号写入更改元数据表。为此,我们对更改元数据编号执行 SELECT MAX,然后将其递增。

我们看到的问题是,事务不知何故从 select max 语句中获取了旧的更改编号。举例说明:

交易1:

开始交易

用 FOR UPDATE 锁定

做事...

获取最新更改编号 (9)

插入编号为 10 的修订

提交

事务 2:

开始交易

用 FOR UPDATE 锁定

做事...

获取最新更改编号 (7)

插入编号为 8 的修订

提交

这会导致事务 2 的修订插入失败,因为更改号是唯一键。我倾向于它是一个可重复读取的问题,但我不确定旧数据如何以这种方式在事务中持续存在。对于每个事务,都有一个 START TRANSACTION 语句,然后父 ID 立即被 FOR UPDATE 锁定。我们有一个具有多个并发事务的高流量站点。任何时候都可能有很多人在等待锁。我很乐意澄清任何一点,并感谢任何人提供的任何见解。

【问题讨论】:

    标签: mysql database transactions locking innodb


    【解决方案1】:

    在更改元数据编号上选择 MAX

    这也需要FOR UPDATE

    另一种方法:

    有一个“序列号生成器”表。

    CREATE TABLE Sequence (
        pk TINYINT NOT NULL,
        seq INT UNSIGNED NOT NULL AUTO_INCREMENT,
        PRIMARY KEY(pk),   -- For ON DUPLICATE KEY UPDATE
        INDEX(seq)         -- Sufficient for AUTO_INCREMENT
    );
    

    唯一的动作(一旦初始化)应该是

    INSERT INTO Sequence (pk, seq) VALUE (1, 0)
        ON DUPLICATE KEY UPDATE seq := LAST_INSERT_ID(seq+1);
    

    这将自动更新一行。然后(在同一连接中),执行此操作以获取新的seq

    SELECT LAST_INSERT_ID();
    

    该声明与连接相关,因此其他人不可能获得您的号码。

    【讨论】:

    • 感谢您的回答!我可以理解锁定实际更改编号的选择。我有点继承了这段代码,所以我不是 100% 为什么要完成某些事情。那些初始的 FOR UPDATE 语句不应该确保在任何时候只有一个事务获得新的更改号吗?
    • 是的,看起来FOR UPDATE 上的SELECTs 应该就足够了。您使用的是什么交易模式。
    • 你指的是隔离模式吗?默认的可重复读取
    • 添加一个 FOR UPDATE 有效地解决了我的问题,虽然我仍然不知道为什么
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-25
    相关资源
    最近更新 更多