从mysql innodb查询中间歇性地获取旧数据

时间:2015-04-03 05:35:37

标签: mysql database transactions locking innodb

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

锁定父行

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

我们看到的问题是某个事务以某种方式从select max语句获得旧的更改号。举例说明:

交易1:

START TRANSACTION

使用FOR UPDATE锁定

做事......

获取最新更改编号(9)

使用数字10插入修订

COMMIT

交易2:

START TRANSACTION

使用FOR UPDATE锁定

做事......

获取最新更改编号(7)

使用数字8插入修订

COMMIT

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

1 个答案:

答案 0 :(得分:1)

  

更改元数据编号上的SELECT 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();

该声明与连接有关,因此其他人无法获得您的号码。