2010-11-08 9 views
5

Ich habe viele Tabellen und aus bestimmten Gründen muss ich Auto-Inkrement-Wert für diese Tabellen beim Start der Anwendung anpassen.Slow auto_increment reset

Ich versuche, dies zu tun:

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

mysql> alter table item auto_increment=1097972232; 

In einer anderen Sitzung:

[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-Tabelle neu zu erstellen beginnt! Warum muss MySQL das tun? Wie kann ich vermeiden, riesige Tabellen neu zu erstellen, während der auto_increment-Wert angepasst wird?

MySQL 5.0, InnoDB.
Tabellendefinition:

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; 

Über warum ich dies zu tun. Lange Rede, kurzer Sinn, ich möchte Abhilfe schaffen mysql innodb Problem über das Zurücksetzen von auto_increment Wert beim Neustart des Servers. Manchmal kopieren wir Zeilen aus unseren Tabellen in andere Tabellen, und wir müssen die Zeilen-ID unverändert lassen. Wenn wir eine Zeile (mit id = 1 zum Beispiel) zu table1 hinzufügen, Zeile in table2 kopieren, Zeile aus table1 löschen und MySQL neu starten, dann wird, wenn wir eine neue Zeile in table1 erstellen, auch diese Zeile id = 1 erhalten. Wenn wir Zeile in Tabelle2 kopieren müssen, erhalten wir eine eindeutige Integritätsverletzung. Wir haben bereits viel Code und es wird schwer sein, alles neu zu schreiben. Das Anpassen des Autoinkrementwerts scheint der einfachste Weg zu sein, um dieses Problem zu beheben.

Hinzugefügt:

MySQL 5.5 - alle gleich :(

+0

MySQL 5.0 ist keine Version, es ist eine ganze Familie von Versionen. Bitte geben Sie alle drei Ziffern der Version an. Wenn Sie nicht wissen, zeigen Sie Variablen wie '% version%' ' – derobert

+3

Auch * Ich muss Auto Inkrement Werte anpassen ... beim Start der Anwendung * scheint mir als * Sie tun es falsch *. – derobert

+2

Warum in jedem Forum gibt es einen Typen, der genau weiß, dass ich es falsch mache ??? :) –

Antwort

12

einfach eine temporäre Datensatz hinzufügen, die auto_increment_id-1 an jedem Tisch, und entfernen Sie die Aufzeichnung danach, schnell und einfach, aber wahrscheinlich zu schmutzig

Beispiel gewünscht hat:

insert into item set id=1097972232-1;

Nach der Ausführung wird der nächste auto_increment 1097972232 sein, was Sie wünschen

das kann Langsamkeit vermeiden

+0

Und dann wahrscheinlich "löschen von Element wo ID = 1097972232-1", so dass Sie keine Junk-Zeile haben. Aber das funktioniert. Ich habe diesen Trick verwendet, um aktiven Einfügungen zu erlauben fortzufahren, während genug Spielraum gelassen wird, um sich in einigen Reihen von einer anderen zu bewegen (wie eine Sicherung, usw.). –

+0

Wow! Wirklich gute Idee. :) Vielen Dank! Ich werde einige Probleme mit Fremdschlüsseln haben, aber das sind kleine Probleme. –

+1

Eigentlich einfügen + Rollback ist genug. –

-1

Ist es nicht:

ALTER TABLE item AUTO_INCREMENT=1; 

Source

+0

Wenn ich diesen Befehl ausführen MySQL rebuilt Tabelle und es dauert eine Menge Zeit .. –

+0

Was meinst du "rebuilt" die Tabelle? Es ändert nur den Standardwert auto_increment. Ist dein Tisch schon "voll"? Wechseln Sie für ein bereits verwendetes Inkrement? Verwenden Sie einen mehrspaltigen Index in dieser Tabelle? – Shikiryu

+0

Ich habe dem Hauptbeitrag zusätzliche Informationen hinzugefügt. –

6

Dies ist ein dokumentiert? "Feature" von MySQL:

Wenn Sie eine andere Option als ALTER TABLE als RENAME verwenden, erstellt MySQL immer eine temporäre Tabelle, auch wenn die Daten nicht unbedingt kopiert werden müssen (z. B. wenn Sie den Namen einer Spalte ändern). Bei MyISAM-Tabellen können Sie die Neugenerierung des Indexes beschleunigen (was der langsamste Teil des Änderungsprozesses ist), indem Sie die Systemvariable myisam_sort_buffer_size auf einen hohen Wert setzen.

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

MySQL 5.1 und 5.5 Unterstützung ein paar alter table Operationen w/o eine temporäre Tabelle, aber die auto_increment Wechsel dokumentiert ist nicht einer von denen zu sein.

Warum müssen Sie den auto_increment-Wert trotzdem ändern? Das sollten Sie nicht routinemäßig tun.

+0

Vielen Dank für den Rat. Morgen werde ich dieses Verhalten auf 5.1 und 5.5 überprüfen. Ich habe den ersten Beitrag aktualisiert, um zu beantworten, warum ich es tun muss. –

+0

Geprüft am 5.5. Egal :( –

1

Wenn Sie eindeutige IDs zwischen zwei oder mehr Servern verwalten müssen, verwenden Sie diese Methode alter table nicht, um auto_increment jedesmal neu zu setzen. Es wäre einfacher, das Inkrement des Inkrements zu ändern, so dass jeder Server eindeutige IDs ohne Intervention generiert. Für zwei Server legen Sie einen Wert für den Start mit 0 und einen Wert für den Start mit 1 fest, wobei ein Inkrement 2 beträgt. Danach werden die gleichen IDs generiert, während der andere Odds generiert. Bei 3 oder mehr Servern, setzen Sie einfach die Anfangswerte auf 0/1/2 in Schritten von 3, für vier ist es 0/1/2/3 mit inc von 4 usw.

Details zu den serverseitigen Einstellungen hier:

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

Auf diese Weise können nur die auto_increment einmal pro Server Tabelle zurücksetzen müssen, und danach werden sie von der Einzigartigkeit Problem automatisch kümmern.

+0

Das macht für mich keinen Sinn. Ich kopiere Zeilen innerhalb einer Datenbank. –

+0

Sie können die verschiedenen Inkremente auch auf verschiedene Tabellen innerhalb derselben DB setzen. Wenn Tabelle A bei 0 beginnt und um 2 inkrementiert, und Tabelle B beginnt bei 1 und wird um 2 erhöht, dann können Sie Zeilen nach Belieben hin- und her kopieren, ohne sich um Schlüsselkollisionen kümmern zu müssen –

+0

In meinem Fall werden IDs nur in Tabelle A generiert. –

3

Es gibt keinen einfachen Weg, um das AUTO_INCREMENT Attribut Standardverhalten in MySQL zu umgehen, und selbst wenn Sie einen Weg finden, würde ich Ihnen nicht empfehlen, dies zu tun, da es der beste Weg ist, um Probleme zu finden kurzfristig. AUTO_INCREMENT Werte sollen in einer Produktionsumgebung nicht angepasst oder zurückgesetzt werden.

Eine mögliche Lösung für Ihr Problem könnte sein, Ihr Modell ein wenig zu denormalisieren. Die Idee ist, das Feld AUTO_INCREMENT in eine Seitentabelle zu verschieben, wo Sie keine Zeilen kopieren oder löschen müssen. Sie müssen dann nur einen neuen ID-Wert aus dieser Seitentabelle abrufen, wenn Sie ein neues Element erstellen, und den vorhandenen ID-Wert beibehalten, wenn Sie Zeilen von einer Tabelle in eine andere kopieren.

Um dies zu erreichen, verwenden wir einen Trigger, der eine neue ID für uns erstellt und sie unserem Artikeldatensatz zuweist. Das ID-Feld der Item-Tabelle muss nullbar sein, damit dies funktioniert. Daher müssen wir den Primärschlüssel durch einen eindeutigen Index ersetzen.

Diese Modellwechsel vollständig transparent für Ihre Anwendung wäre, so würden Sie keine Änderung in Ihrem Anwendungscode zu machen.

Hier sind einige Beispielskripte. Lassen Sie uns sagen, dass wir zwei Postentabellen in unserer Datenbank haben, mit einigen gemeinsamen Zeilen und einige Zeilen, die von der ersten Tabelle auf den zweiten Tisch bewegt werden müssen:

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

Wenn wir versuchen, einige Daten von einem Tisch zum anderen zu bewegen und dann starten Sie Ihren Server neu, wir werden das Problem von AUTO_INCREMENT Wert zurücksetzen finden. So werden wir leicht unser Modell wie folgt ändern:

New model with side table

Wir werden in mehreren Schritten vorgehen, um unser Datenmodell zu migrieren. Die DDL-Anweisungen in den folgenden Migrationsskripten wurden unter Verwendung der IDE generiert.

  • Zuerst erstellen wir eine neue item_keys Tabelle, die die AUTO_INCREMENT Feld halten wird:
 
-- 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); 
  • Aber bevor das AUTO_INCREMENT Attribut aktivieren, müssen wir bestehende ids in unsere neue Tabelle einfügen:
 
-- 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 
; 
  • Wir aktivieren jetzt die AUTO_INCREMENT Attribut und seinen Wert für zukünftige Einsätze initialisieren:
 
-- 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; 
  • Dann können wir die item1 und item2 Tabellen ändern, um die Primärschlüssel durch einen eindeutigen Index zu ersetzen und Referenz der Primärschlüssel der Tabelle 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) 
; 
Schließlich
  • , wir müssen nur die Trigger erstellen, die die IDs Schöpfung für uns gelingen wird:
 
-- 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; 
|; 

Jetzt können wir Daten von einem Tisch zum anderen bewegen, halten die ids unverändert, und wenn wir den Server neu starten, bleibt der Wert AUTO_INCREMENT in item_keys gleich.

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

Vielen Dank für den Vorschlag, das ist eine gute Idee , aber dieser Ansatz wird einige Probleme mit der Leistung verursachen. –

+0

@Andrew, welche Stufe der Leistung benötigen Sie für welche Art von Operation (Elemente einfügen, Elemente Masseneinfügung, Elemente löschen, Elemente Auswahl)? Können Sie einige Kennzahlen zur Verfügung stellen? Haben Sie Führen Sie einen Benchmark, so dass Sie sagen können, dass dies nicht gut genug ist? –

+0

@Andrew, würde ich verstehen, wenn Sie diese Lösung zu kompliziert im Vergleich zu th gefunden Die temporäre Aufzeichnung einfügen, aber ich verstehe das Argument der Leistung nicht. –