MySQL在没有自动增量的情况下获取下一个唯一值

dan*_*gel 7 mysql constraint auto-increment locking

我有一个 MySQL 数据库 (InnoDB),我正在开发它用于跟踪我组织的工作订单。有多个“站点”,每个站点的工作订单号从 100 开始。

WorkorderID    SiteID    SiteWorkorderNum
     1           1            100
     2           1            101
     3           2            100
Run Code Online (Sandbox Code Playgroud)

WorkorderID是自动增量字段。
SiteIDSiteWorkorderNum都设置为唯一约束。

每当即将为特定站点插入新工作订单时,应用程序都会检查登录者的站点 ID 的 max(SiteWorkorderNum) 并将该值返回到插入语句。我想避免的问题是,同一站点的两个用户是否同时输入新的工作订单。

我有几个可能的解决方案,但我想看看是否有更好的方法,因为每个方法都有其缺点,但我确信一个会比另一个更好,当然我对新想法持开放态度。

1)锁定表以保证在我们检索并插入我们的值之前没有其他用户检索 SiteWorkorderNum 值(但是假设我们每分钟有成千上万的用户试图输入工作订单,表锁定不会减慢速度吗? )

2)不要锁定表,检索下一个可用的 SiteWorkorderNum 并尝试插入到数据库中,如果我收到一个唯一约束错误,请捕获该错误并重试,直到我成功。(这可能会释放表,但再次假设我们在同一站点有数千个用户,多个数据库调用是否会有点低效?)

我是否对这些解决方案中的任何一个“可能”影响性能过于偏执?当然,我没有成千上万的用户,但我想在这个设计中应用最佳实践,以防我曾经在一个流量很高的项目上工作。

Rol*_*DBA 4

这种情况的有趣之处在于可以使用 MyISAM 存储来解决。

我在 2012 年 4 月回答过这样一个问题:一张表里怎么会有两个自增列?您需要创建一张表,其唯一目的是为每个站点创建工作订单序列

CREATE TABLE site_workorder_seq
(
    SiteID int not null,
    SiteWorkorderNum int not null auto_increment,
    PRIMARY KEY (SiteID,SiteWorkorderNum)
) ENGINE=MyISAM;
Run Code Online (Sandbox Code Playgroud)

以下是加载到该表中的示例:

mysql>     DROP DATABASE david;
Query OK, 1 row affected (0.01 sec)

mysql>     CREATE DATABASE david;
Query OK, 1 row affected (0.00 sec)

mysql>     USE david
Database changed
mysql>     CREATE TABLE site_workorder_seq
    ->     (
    ->         SiteID int not null,
    ->         SiteWorkorderNum int not null auto_increment,
    ->         PRIMARY KEY (SiteID,SiteWorkorderNum)
    ->     ) ENGINE=MyISAM;
Query OK, 0 rows affected (0.01 sec)

mysql>     INSERT INTO site_workorder_seq (SiteID) VALUES
    ->     (1),(1),(2),(3),(3),(3),(3),(4),(4),(4),
    ->     (5),(5),(4),(2),(2),(2);
Query OK, 16 rows affected (0.00 sec)
Records: 16  Duplicates: 0  Warnings: 0

mysql>     SELECT * FROM site_workorder_seq;
+--------+------------------+
| SiteID | SiteWorkorderNum |
+--------+------------------+
|      1 |                1 |
|      1 |                2 |
|      2 |                1 |
|      2 |                2 |
|      2 |                3 |
|      2 |                4 |
|      3 |                1 |
|      3 |                2 |
|      3 |                3 |
|      3 |                4 |
|      4 |                1 |
|      4 |                2 |
|      4 |                3 |
|      4 |                4 |
|      5 |                1 |
|      5 |                2 |
+--------+------------------+
16 rows in set (0.00 sec)

mysql>
Run Code Online (Sandbox Code Playgroud)

让我们看看每个站点的最后一个 WorkorderNum

mysql> SELECT SiteID,MAX(SiteWorkorderNum) SiteWorkorderNum
    -> FROM site_workorder_seq GROUP BY SiteID;
+--------+------------------+
| SiteID | SiteWorkorderNum |
+--------+------------------+
|      1 |                2 |
|      2 |                4 |
|      3 |                4 |
|      4 |                4 |
|      5 |                2 |
+--------+------------------+
5 rows in set (0.05 sec)

mysql>
Run Code Online (Sandbox Code Playgroud)

现在,假设您想要获取 SiteID 3 的下一个 SiteWorkorderNum。您可以这样做:

INSERT INTO site_workorder_seq (SiteID) VALUES (3);
SELECT MAX(SiteWorkorderNum) INTO @nextworkordernum
FROM site_workorder_seq WHERE SiteID=3;
SELECT @nextworkordernum;
Run Code Online (Sandbox Code Playgroud)

让我们运行一下看看会发生什么

mysql> INSERT INTO site_workorder_seq (SiteID) VALUES (3);
Query OK, 1 row affected (0.00 sec)

mysql> SELECT MAX(SiteWorkorderNum) INTO @nextworkordernum
    -> FROM site_workorder_seq WHERE SiteID=3;
Query OK, 1 row affected (0.00 sec)

mysql> SELECT @nextworkordernum;
+-------------------+
| @nextworkordernum |
+-------------------+
|                 5 |
+-------------------+
1 row in set (0.03 sec)

mysql> SELECT * FROM site_workorder_seq;
+--------+------------------+
| SiteID | SiteWorkorderNum |
+--------+------------------+
|      1 |                1 |
|      1 |                2 |
|      2 |                1 |
|      2 |                2 |
|      2 |                3 |
|      2 |                4 |
|      3 |                1 |
|      3 |                2 |
|      3 |                3 |
|      3 |                4 |
|      3 |                5 |
|      4 |                1 |
|      4 |                2 |
|      4 |                3 |
|      4 |                4 |
|      5 |                1 |
|      5 |                2 |
+--------+------------------+
17 rows in set (0.00 sec)

mysql> SELECT SiteID,MAX(SiteWorkorderNum) SiteWorkorderNum
    -> FROM site_workorder_seq GROUP BY SiteID;
+--------+------------------+
| SiteID | SiteWorkorderNum |
+--------+------------------+
|      1 |                2 |
|      2 |                4 |
|      3 |                5 |
|      4 |                4 |
|      5 |                2 |
+--------+------------------+
5 rows in set (0.00 sec)

mysql>
Run Code Online (Sandbox Code Playgroud)

只要您使用除了所有 InnoDB 表之外的这一 MyISAM,您就可以根据您的喜好为每个站点生成 workordernums。