2010-11-08 40 views
5

我有很多的表,由于某些原因,我需要调整应用程序启动时这个表的自动增量值。缓慢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; 

在另一个会话:

[email protected]:~$ 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; 

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

补充:

的MySQL 5.5 - 所有相同:(

+0

MySQL 5.0不是一个版本,它的一个完整的版本系列。请提供版本的全部三位数字。如果你不知道,请'显示像'%version%'这样的变量'' – derobert 2010-11-10 17:33:42

+3

另外,*我需要调整自动递增值...在应用程序启动时*打击我*因为你做错了*。 – derobert 2010-11-10 17:54:27

+2

为什么在每个论坛上都有一个人,谁知道我做错了? :) – 2010-11-10 20:13:34

回答

12

只需添加具有所需auto_increment_id-1每个表, 临时记录和删除后,方便快捷的记录,但可能太肮脏

例如:

insert into item set id=1097972232-1;

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

这可以避免缓慢

+0

然后可能从项目id = 1097972232-1'删除,所以你没有垃圾行。但这确实有用。我已经使用这个技巧来允许活动插入继续,同时留下足够的空间来移动另一些行(比如备份等)。 – 2010-11-14 01:16:11

+0

哇!真是个好主意。 :) 非常感谢你!外键我会遇到一些麻烦,但这是小问题。 – 2010-11-14 10:34:03

+1

其实插入+回滚就足够了。 – 2010-11-15 08:59:12

-1

是不是:

ALTER TABLE item AUTO_INCREMENT=1; 

Source

+0

当我运行这个命令MySQL重建表,它需要很多时间.. – 2010-11-08 15:01:12

+0

你是什么意思“重建”表?它只是改变了auto_increment默认值。你的桌子已经“满”了吗?你正在改变已经使用的增量吗?你在这个表格中使用多列索引吗? – Shikiryu 2010-11-08 15:51:45

+0

我向主帖添加了附加信息。 – 2010-11-09 08:05:04

6

这是一个记录? MySQL的“特性”:

如果你使用任何选项ALTER比RENAME其他表时,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操作W/O临时表,但改变AUTO_INCREMENT未记录是其中的一个。

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

+0

谢谢你的建议。明天我会在5.1和5.5上检查这个行为。我更新了第一篇文章,回答为什么我必须这样做。 – 2010-11-10 20:11:58

+0

检查5.5。全部相同:( – 2010-11-13 10:32:57

1

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

详情:

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

这种方式,你只需要以每桌每一次服务器重置AUTO_INCREMENT,后来他们会自动照顾唯一的问题。

+0

这对我没有意义我在一个数据库中复制行 – 2010-11-12 21:40:18

+0

您可以在同一个数据库中的不同表上设置不同的增量如果表A从0开始并增加2,而表B从1开始并增加2,那么你可以随意来回复制行,而不必担心关键冲突。 – 2010-11-13 04:48:19

+0

在我的情况下,ID只在表A中生成。 – 2010-11-13 10:50:50

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; 
|; 

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

 
-------------- 
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) 
+0

谢谢你的建议。 ,但是这种方法会带来一些性能问题 – 2010-11-14 10:35:57

+0

@Andrew,你需要什么样的性能来表示什么类型的操作(项目插入,项目批量插入,项目删除,项目选择)?你能提供一些指标吗?运行一个基准,所以你可以说这是不够好? – 2010-11-14 10:55:34

+0

@安德鲁,我会明白,如果你发现这个解决方案太复杂临时记录插入,但我不明白表现的论点。 – 2010-11-14 11:02:03