我们有各种表来表示各种类型的数据。每个表都有一个相应的修订表来跟踪此数据的历史记录。每个修订版(修订表中的条目)都有一个唯一的编号。此编号存储在更改元数据表中。这些表中的每一个都引用了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。我们有一个包含多个并发事务的高流量站点。任何时候都有可能有很多人在等待锁定。我很乐意澄清任何一点,并希望任何人都可以提供任何见解。
答案 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();
该声明与连接有关,因此其他人无法获得您的号码。