【问题标题】:Safely auto increment MySQL field based on MAX() subquery upon insert插入时基于 MAX() 子查询安全地自动递增 MySQL 字段
【发布时间】:2013-12-11 12:29:48
【问题描述】:

我有一个表,其中包含一个标准的自动递增 ID、一个类型标识符、一个数字和一些其他不相关的字段。当我在这个表中插入一个新对象时,数字应该根据类型标识符自动增加。

以下是输出应如何显示的示例:

id      type_id     number
1       1           1
2       1           2
3       2           1
4       1           3   
5       3           1
6       3           2
7       1           4
8       2           2

如您所见,每次插入新对象时,数字都会根据 type_id 递增(即,如果我插入 type_id 为 1 的对象并且已经有 5 个对象匹配此 type_id,则新对象上的数字应该是 6)。

我正在尝试找到一种高性能的方式来实现大量并发。例如,同一秒内可能有 300 次插入相同的 type_id,它们需要按顺序处理。

我已经尝试过的方法:

PHP

这是一个坏主意,但为了完整起见,我添加了它。已请求获取项目类型的 MAX() 编号,然后将编号 + 1 作为插入的一部分添加。这很快但不能同时工作,因为在对 MAX() 的请求和导致具有相同编号和 type_id 的多个对象的特定插入之间可能有 200 次插入。

锁定

在每次插入之前和之后手动锁定和解锁表,以保持增量。由于并发插入的数量以及在整个应用程序中不断读取表,这会导致性能问题。

子查询交易

这就是我目前的做法,但它仍然会导致大量性能问题:

START TRANSACTION;
INSERT INTO objects (type_id,number) VALUES ($type_id, (SELECT COALESCE(MAX(number),0)+1 FROM objects WHERE type_id = $type_id FOR UPDATE));
COMMIT;

这种方法的另一个负面影响是我需要进行后续查询才能获得添加的数字(即搜索具有按数字 desc 排序的 $type_id 的对象,以便我可以看到之前的数字创建 - 这是基于 $user_id 完成的,因此它可以工作,但添加了一个我想避免的额外查询)

触发器

我研究过使用触发器以便在插入时动态添加数字,但这并不高效,因为我需要对要插入的表执行查询(这是不允许的,因此必须在导致性能问题的子查询)。

分组自动增量

我查看了分组自动增量(以便数字会根据 type_id 自动增量),但后来我丢失了我的自动增量 ID。


是否有人对我如何在我需要的并发插入级别上实现此性能有任何想法?我的表目前是 MySQL 5.5 上的 InnoDB

感谢任何帮助!

更新:以防万一,对象表中有几百万个对象。一些type_id 可以分配大约500,000 个对象。

【问题讨论】:

  • 我的想法是 - 使用组自动增量。那就是 - 关于 - 如果您的 <type_id, number> 是唯一的(并且它必须是 - 由于您的逻辑) - 它可以是主键,那么代理 id 键的意义是什么?您甚至在寻找一种方法来使用您的组合来确定正确的行地址,因此 id 是多余的。如果它的目的是保持连续编号 - 为什么不在应用程序中应用它?所以看起来你的问题是关于如何在表中维护两个主键? - 这是设计上的错误
  • 这听起来很有趣。我对这个更改的问题是应用程序已经在运行,并且有几百万个带有我要加入的 ID 的对象。作为一个基本示例,每个对象都可以附加许多“评论”,这些“评论”是通过在评论表上添加object_id 来完成的。如果我删除现有的主键并使用<type_id,number>,我将如何将这两个链接在一起?我以前从未使用过标准 id 模式以外的任何东西,主要是自动递增的。
  • 嗯,这可能是一个想法,但你有InnoDB。它不支持多列 auto_increment。所以这不是一个选择,对不起
  • 从技术上讲,我可以将 id 列从 int 更改为 varchar,然后在插入时生成 UUID。这将允许我保留代理键,以便我以前的所有联接等都可以工作,但这意味着我不会有两个自动增量?你认为这有意义吗?
  • 您不能使用 auto_increment 自动生成 number 值,因为该机制仅适用于 BDBMyISAM storage-engines

标签: php mysql auto-increment


【解决方案1】:

带有子查询的事务

尝试在 type_id 列上创建 index

我认为通过在 type_id 列上建立索引可以加快您的子查询速度。

【讨论】:

  • 已经有索引但是还是太慢了。该表中有几百万个条目。
  • 我可以查看所有列和数据类型的表结构吗??
【解决方案2】:

使用事务并选择 ... 进行更新。这将解决并发冲突。

【讨论】:

  • 正如我的问题中提到的,我已经在这样做了(代码甚至在问题中)但它会导致性能问题,因为它似乎无法在同时非常好。
  • 可能你的选择语句很慢?您提到,您在 type_id 上使用索引,可能是 type_id 上的两列索引,并且 number 会有所帮助吗?
【解决方案3】:
 DROP TABLE IF EXISTS my_table;

 CREATE TABLE my_table 
 (id      INT NOT NULL AUTO_INCREMENT PRIMARY KEY
 ,type_id     INT NOT NULL
 );

 INSERT INTO my_table VALUES
 (1,1),(2,1),(3,2),(4,1),(5,3),(6,3),(7,1),(8,2);

 SELECT x.*
      , COUNT(*) rank 
   FROM my_table x 
   JOIN my_table y 
     ON y.type_id = x.type_id 
    AND y.id <= x.id 
  GROUP 
     BY id 
  ORDER 
     BY type_id
      , rank;

 +----+---------+------+
 | id | type_id | rank |
 +----+---------+------+
 |  1 |       1 |    1 |
 |  2 |       1 |    2 |
 |  4 |       1 |    3 |
 |  7 |       1 |    4 |
 |  3 |       2 |    1 |
 |  8 |       2 |    2 |
 |  5 |       3 |    1 |
 |  6 |       3 |    2 |
 +----+---------+------+

或者,如果性能是一个问题,只需使用几个@variables 做同样的事情。

【讨论】:

  • 我想你误解了我的问题。我希望在插入时将数字存储在数据库中,而不是之后从选择中计算出来。
  • 因为需要存储对象的编号。表中有几百万个对象,所以我不想每次显示时都动态生成它。
【解决方案4】:

也许一个想法是为所有具有共同“type_id”的行创建一个(临时)表。 在该表中,您可以对 num 列使用自动递增。 那么你的 num 应该是完全值得信赖的。 然后您可以选择您的数据并更新您的第一个表。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-22
    • 1970-01-01
    • 1970-01-01
    • 2011-01-03
    相关资源
    最近更新 更多