2010-11-08 72 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