【问题标题】:Prevent duplicate increment values during parallel transactions防止并行事务期间重复的增量值
【发布时间】:2024-10-10 14:50:02
【问题描述】:

我在 MySQL 中使用事务来存储订单。每个订单都有OrderID (BIGINT),如下所示:XXXXXX0001,后四位递增(1620200001、1620200002、1620200003、...)。

交易如下:

start transaction
get new OrderID (increment by 1)
do some stuff
commit/rollback

保存交易可能需要几秒钟,如果在很短的时间内创建了多个订单,则可以将重复的OrderID 插入数据库。在提交第一个订单之前,第二个被分配了相同的OrderID,目前是下一个订单。

防止这种情况的最佳方法是什么?拥有UNIQUE OrderID 并不能解决它(第二个顺序会有回滚)。我可以摆脱事务并更快地保存OrderID,但这会导致其他潜在问题并且不能完全解决这个问题(只是减少了问题发生的机会)。

任何帮助将不胜感激。

【问题讨论】:

    标签: mysql transactions auto-increment


    【解决方案1】:

    了解AUTO_INCREMENT。在CREATE TABLE 的手册中搜索它。这是一个长页面,但 AUTO_INCREMENT 记录在页面下方的 1/4 左右。

    简而言之,您只需使用列选项声明主键:

    CREATE TABLE mytable (
      id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
      ...other columns... 
    );
    

    初始值为1,或者你可以让它从更高的值开始:

    ALTER TABLE mytable AUTO_INCREMENT=1620200001;
    

    具有自动递增列的表可确保每个并发事务获得唯一的递增值。没有竞争条件,因为 INSERT 获得了一个简短的表锁,在此期间它增加了值。与基于事务的锁不同,自增表锁是立即释放的。因此并发会话不必等待您的事务完成。

    保证自动增量是唯一的。也就是说,不会将相同的值分配给多个会话。但是,不能保证分配 连续 值。此外,它可能会为一个会话分配一个值,但该会话决定回滚其事务。它分配的值不会返回到任何类型的值队列,因为在此期间可能有其他会话分配了接下来的几个值。因此,有可能“丢失”值,然后您的表有“间隙”或不连续的值。

    不要担心差距。即使值是连续的,这些也可能发生,因为您可能稍后会删除一行。

    【讨论】:

    • 谢谢比尔,不幸的是,该表似乎只能有一个 AUTO_INCREMENT 列(我需要更多,每个订单都有唯一的 OrderID,有些有唯一的 DocumentID,也需要递增)。但你的回答回答了我写的问题,所以我接受了。
    • 正确,在 MySQL 中,您只能将一列声明为每个表的自动增量。如果您需要其他解决方案,您可以使用 PostgreSQL 并创建不同的序列对象。