慢auto_increment重置

时间:2010-11-08 14:41:58

标签: mysql innodb auto-increment

我有很多表,由于某些原因,我需要在应用程序启动时调整此表的自动增量值。

我尝试这样做:

mysql> select max(id) from item;
+----------+
| max(id)  |
+----------+
| 97972232 |
+----------+
1 row in set (0.05 sec)

mysql> alter table item auto_increment=1097972232;

在另一场会议中:

afrolov@A1-DB1:~$ mysql -u root -e "show processlist" | grep auto_increment
472196  root    localhost       test    Query   39      copy to tmp table       alter table item auto_increment=1097972232

MySQL开始重建表!为什么MySQL需要这样做?如何在调整auto_increment值时避免重建大表?

MySQL 5.0,InnoDB。
表定义:

 CREATE TABLE `item` (
      `id` bigint(20) NOT NULL auto_increment,
      `item_res_id` int(11) NOT NULL default '0',
      `stack_count` int(11) NOT NULL default '0',
      `position` int(11) NOT NULL default '0',
      `place` varchar(15) NOT NULL default '',
      `counter` int(11) NOT NULL default '-1',
      `is_bound` tinyint(4) NOT NULL default '0',
      `remove_time` bigint(20) NOT NULL default '-1',
      `rune_res_id` int(11) default NULL,
      `rune_id` bigint(20) default NULL,
      `avatar_id` bigint(20) NOT NULL,
      `rune_slot_res_id` int(11) default NULL,
      `is_cursed` tinyint(4) NOT NULL,
      PRIMARY KEY  (`id`),
      UNIQUE KEY `avatar_id` (`avatar_id`,`place`,`position`),
      UNIQUE KEY `rune_id` (`rune_id`),
      KEY `idx_item_res_id` (`item_res_id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=97972233 DEFAULT CHARSET=utf8;

关于我为什么要这样做。长话短说我想解决关于在服务器重启时重置auto_increment值的mysql innodb问题。有时我们将行从表中复制到另一个表,我们必须保持行ID不变。当我们向table1添加一行(例如id = 1)时,将行复制到table2,从table1中删除行并重新启动MySQL,然后当我们在table1中创建一个新行时,此行也将获得id = 1。因此,如果我们必须将行复制到table2,我们会遇到唯一的约束违规。我们已经有很多代码,很难重写它们。调整自动增量值似乎是解决此问题的最简单方法。

添加了:

MySQL 5.5 - 完全相同:(

5 个答案:

答案 0 :(得分:13)

只需向每个表添加一个需要auto_increment_id-1的临时记录, 并在此之后删除记录,快速简便,但可能太脏了

示例:

insert into item set id=1097972232-1;

执行后,下一个auto_increment将是1097972232,这是你想要的

这可以避免缓慢

答案 1 :(得分:6)

这是MySQL记录的“功能”:

  

如果对RENAME之外的ALTER TABLE使用任何选项,MySQL总是会创建一个临时表,即使数据不需要严格复制(例如更改列的名称)。对于MyISAM表,您可以通过将myisam_sort_buffer_size系统变量设置为较高值来加快索引重新创建操作(这是更改过程中最慢的部分)。

http://dev.mysql.com/doc/refman/5.0/en/alter-table.html

MySQL 5.1和5.5支持一些没有临时表的alter table操作,但更改auto_increment没有记录为其中之一。

为什么还需要更改auto_increment值?这不是你应该经常做的事情。

答案 2 :(得分:3)

没有简单的方法可以绕过MySQL中的AUTO_INCREMENT属性默认行为,即使你找到了办法,我也不建议你这样做,因为这是遇到它的最佳方法。短期内的问题。 AUTO_INCREMENT值不应在生产环境中进行调整或重置。

问题的一个可能解决方案可能是对模型进行非规范化。我们的想法是将AUTO_INCREMENT字段移动到您不必复制或删除行的边表。您所要做的就是在创建新项时从此边表获取新的id值,并在将行从一个表复制到另一个表时保留现有的id值。

为了达到这个目的,我们将使用一个触发器为我们创建一个新的id并将其分配给我们的项目记录。项目表的id字段必须是可空的才能使其工作,因此我们必须用唯一索引替换主键。

此模型更改对您的应用程序完全透明,因此您的应用程序代码无需更改

以下是一些示例脚本。假设我们的数据库中有两个项目表,有一些常见的行,有些行需要从第一个表移到第二个表:

CREATE TABLE `item1` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `item_res_id` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `item2` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `item_res_id` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO item1 (item_res_id) VALUES (1);
INSERT INTO item1 (item_res_id) VALUES (2);
INSERT INTO item2 (item_res_id) VALUES (1);

如果我们尝试将一些数据从一个表移动到另一个表然后重新启动服务器,我们将遇到AUTO_INCREMENT值重置的问题。所以我们将稍微修改我们的模型如下:

New model with side table

我们将分几个步骤迁移我们的数据模型。以下迁移脚本中的DDL语句是使用neXtep Designer IDE生成的。

  • 首先,我们创建一个新的item_keys表,其中包含AUTO_INCREMENT字段:
-- Creating table 'item_keys'
CREATE TABLE item_keys ( 
   id BIGINT(20) UNSIGNED NOT NULL
  ,key_ctime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
) Engine=InnoDB default charset=utf8;

-- Creating Primary Key constraint 'PRIMARY' on table 'item_keys'
ALTER TABLE item_keys ADD CONSTRAINT PRIMARY KEY (id);
  • 但在激活AUTO_INCREMENT属性之前,我们必须将现有ID插入新表:
-- Initializing item_keys with existing ids
INSERT INTO item_keys (id)
    SELECT i1.id
    FROM item1 i1
        LEFT JOIN item_keys ik ON ik.id = i1.id
    WHERE ik.id IS NULL
;

INSERT INTO item_keys (id)
    SELECT i2.id
    FROM item2 i2
        LEFT JOIN item_keys ik ON ik.id = i2.id
    WHERE ik.id IS NULL
;
  • 我们现在可以激活AUTO_INCREMENT属性,并为将来的插入初始化其值:
-- Activating auto_increment constraint...
ALTER TABLE item_keys MODIFY id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT;

-- Initializing auto_increment value
SELECT @inc_value := MAX(id) FROM item_keys;
SET @alter_query = CONCAT('ALTER TABLE item_keys AUTO_INCREMENT=',@inc_value); 
PREPARE alter_query FROM @alter_query; 
EXECUTE alter_query; 
DEALLOCATE PREPARE alter_query; 
  • 然后我们可以更改item1item2表,用唯一索引替换主键,并引用item_keys表的主键:
-- De-activating auto_increment constraint...
ALTER TABLE item1 MODIFY id BIGINT(20) UNSIGNED NOT NULL;
-- Dropping constraint 'PRIMARY'...
ALTER TABLE item1 DROP PRIMARY KEY;
ALTER TABLE item1 MODIFY id BIGINT(20) UNSIGNED NULL;
-- Creating index 'item1_uk'...
CREATE UNIQUE INDEX item1_uk ON item1 (id);
-- Creating Foreign Key constraint 'item1_keys_fk' on table 'item1'
ALTER TABLE item1 ADD 
   CONSTRAINT item1_keys_fk FOREIGN KEY item1_keys_fk
      (id) REFERENCES item_keys
      (id)
;
-- De-activating auto_increment constraint...
ALTER TABLE item2 MODIFY id BIGINT(20) UNSIGNED NOT NULL;
-- Dropping constraint 'PRIMARY'...
ALTER TABLE item2 DROP PRIMARY KEY;
ALTER TABLE item2 MODIFY id BIGINT(20) UNSIGNED NULL;
-- Creating index 'item2_uk'...
CREATE UNIQUE INDEX item2_uk ON item2 (id);
-- Creating Foreign Key constraint 'item2_keys_fk' on table 'item2'
ALTER TABLE item2 ADD 
   CONSTRAINT item2_keys_fk FOREIGN KEY item2_keys_fk
      (id) REFERENCES item_keys
      (id)
;
  • 最后,我们只需创建将为我们管理ID创建的触发器:
-- Creating trigger 'tr_item1_bi' on table 'item1'...
DELIMITER |;
CREATE TRIGGER tr_item1_bi BEFORE INSERT ON item1
FOR EACH ROW
BEGIN
   IF (NEW.id IS NULL) THEN

        -- If no item id has been specified in the INSERT statement, it
        -- means we want to create a new item. We insert a new record
        -- into the item_keys table to get an item id.
        INSERT INTO item_keys (
            key_ctime
          )
        VALUES (NOW());

        SET NEW.id = LAST_INSERT_ID();
    END IF;
END;
|;
-- Creating trigger 'tr_item2_bi' on table 'item2'...
DELIMITER |;
CREATE TRIGGER tr_item2_bi BEFORE INSERT ON item2
FOR EACH ROW
BEGIN
   IF (NEW.id IS NULL) THEN

        -- If no item id has been specified in the INSERT statement, it
        -- means we want to create a new item. We insert a new record
        -- into the item_keys table to get an item id.
        INSERT INTO item_keys (
            key_ctime
          )
        VALUES (NOW());

        SET NEW.id = LAST_INSERT_ID();
    END IF;
END;
|;

现在我们可以将数据从一个表移动到另一个表,保持id不变,如果我们重新启动服务器,AUTO_INCREMENT中的item_keys值将保持不变。

--------------
INSERT INTO item2
    SELECT i1.*
    FROM item1 i1
        LEFT JOIN item2 i2
            ON i2.id = i1.id
    WHERE i2.id IS NULL
--------------
Query OK, 1 row affected (0.04 sec)
Records: 1  Duplicates: 0  Warnings: 0

--------------
DELETE FROM item1
--------------
Query OK, 2 rows affected (0.00 sec)

--------------
INSERT INTO item1 (item_res_id) VALUES (3)
--------------
Query OK, 1 row affected (0.00 sec)

--------------
SELECT * FROM item1
--------------

+------+-------------+
| id   | item_res_id |
+------+-------------+
|    3 |           3 |
+------+-------------+
1 row in set (0.00 sec)

--------------
SELECT * FROM item2
--------------

+------+-------------+
| id   | item_res_id |
+------+-------------+
|    1 |           1 |
|    2 |           2 |
+------+-------------+
2 rows in set (0.00 sec)

--------------
SELECT * FROM item_keys
--------------

+----+---------------------+
| id | key_ctime           |
+----+---------------------+
|  1 | 2010-11-14 10:31:21 |
|  2 | 2010-11-14 10:31:21 |
|  3 | 2010-11-14 10:31:46 |
+----+---------------------+
3 rows in set (0.00 sec)

答案 3 :(得分:1)

如果需要在两个或更多服务器之间维护唯一ID,请不要每次都使用此alter table方法重置auto_increment。更改增量的增量会更容易,这样每个服务器都可以生成唯一的ID而无需干预。对于两台服务器,您设置一个从0开始,一个从1开始,增量为2 - 之后一个将生成偶数ID,另一个将生成赔率。使用3个或更多服务器,您只需将初始值设置为0/1/2,增量为3,其中4为0/1/2/3,inc为4,等等...

此处有关服务器端设置的详细信息:

http://dev.mysql.com/doc/refman/5.1/en/replication-options-master.html#sysvar_auto_increment_increment

这样你只需每个服务器每个表重置一次auto_increment,然后他们会自动处理唯一性问题。

答案 4 :(得分:-1)

不是吗:

ALTER TABLE item AUTO_INCREMENT=1;

Source