2010-11-08 6 views
5

Ho un sacco di tabelle e per alcuni motivi ho bisogno di regolare il valore di incremento automatico per queste tabelle all'avvio dell'applicazione.Reset autoincremento lento

provo a fare questo:

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

mysql> alter table item auto_increment=1097972232; 

In un'altra sessione:

[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 sta cominciando a ricostruire la tavola! Perché MySQL ha bisogno di farlo? Come posso evitare di ricostruire tabelle enormi durante la regolazione del valore auto_increment?

MySQL 5.0, InnoDB. Definizione
Tabella:

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; 

proposito perché devo fare questo. Per farla breve voglio risolvere il problema di mysql innodb sul ripristino del valore auto_increment sul riavvio del server. A volte copiamo le righe dalle nostre tabelle in altre tabelle e dobbiamo mantenere invariato l'ID delle righe. Quando aggiungiamo una riga (con id = 1 per esempio) a table1, copia riga a table2, cancella riga da table1 e riavvia MySQL, quindi quando creiamo una nuova riga in table1 questa riga otterrà id = 1 anche. Quindi, se dovessimo copiare la riga su table2, avremo una violazione del vincolo univoco. Abbiamo già un sacco di codice e sarà difficile riscrivere tutto. La regolazione del valore di autoincremento sembra il modo più semplice per risolvere questo problema.

Aggiunto:

MySQL 5.5 - tutti lo stesso :(

+0

MySQL 5.0 non è una versione, è un'intera famiglia di versioni. Si prega di fornire tutte e tre le cifre della versione. Se non lo sai, fai 'mostra variabili come '% versione%'' – derobert

+3

Inoltre, * Ho bisogno di regolare i valori di incremento automatico ... all'avvio dell'applicazione * mi sembra che * stai facendo male *. – derobert

+2

Perché su ogni forum c'è un ragazzo, che sa esattamente di quanto sto facendo male ??? :) –

risposta

12

semplicemente aggiungere record di temporanea che ha desiderato auto_increment_id-1 ad ogni tavolo, e rimuovere il record dopo quello, semplice e veloce, ma probabilmente troppo sporca

esempio:

insert into item set id=1097972232-1;

dopo l'esecuzione, il prossimo auto_incremento sarà 1097972232, che è quello che desideri

questo può evitare la lentezza

+0

E quindi probabilmente 'elimina dall'elemento dove id = 1097972232-1' in modo da non avere una riga di posta indesiderata. Ma questo funziona. Ho usato questo trucco per consentire agli inserti attivi di continuare lasciando spazio sufficiente per spostarsi in alcune file da un altro (come un backup, ecc.). –

+0

Wow! Davvero una buona idea :) Grazie mille! Avrò dei problemi con le chiavi esterne, ma questo è un problema minore. –

+1

In realtà inserire + rollback è sufficiente. –

-1

Non è:

ALTER TABLE item AUTO_INCREMENT=1; 

Source

+0

Quando eseguo questo comando, ricompiliamo la tabella MySQL e ci vuole un sacco di tempo .. –

+0

Cosa intendi per "ricostruire" il tavolo? Cambia solo il valore predefinito auto_increment. Il tuo tavolo è già "pieno"? Stai cambiando per un incremento già utilizzato? Stai usando un indice a più colonne in questa tabella? – Shikiryu

+0

Ho aggiunto ulteriori informazioni al post principale. –

6

Questo è un documentato? "funzionalità" di MySQL:

Se si utilizza qualsiasi opzione per ALTER TABLE diversa da RENAME, MySQL crea sempre una tabella temporanea, anche se non è necessario copiare i dati (ad esempio quando si modifica il nome di una colonna). Per le tabelle MyISAM, è possibile velocizzare l'operazione di ri-creazione dell'indice (che è la parte più lenta del processo di modifica) impostando la variabile di sistema myisam_sort_buffer_size su un valore elevato.

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

MySQL 5.1 e 5.5 il supporto di un paio di operazioni ALTER TABLE w/o una tabella temporanea, ma cambiando l'auto_increment non è documentato ad essere uno di quelli.

Perché è necessario modificare il valore auto_increment, comunque? Questo non è qualcosa che dovresti fare di routine.

+0

Grazie per il consiglio. Domani controllerò questo comportamento su 5.1 e 5.5. Ho aggiornato il primo post per rispondere perché devo farlo. –

+0

Controllato su 5.5. ( –

1

Se è necessario mantenere ID univoci tra due o più server, non utilizzare questo metodo alter table per reimpostare auto_increment ogni volta. Sarebbe più semplice modificare l'incremento dell'incremento in modo che ogni server generi ID univoci senza intervento. Per due server, ne imposti uno per iniziare da 0 e uno per iniziare da 1, con un incremento di 2 - in seguito uno genererà anche ID, l'altro genererà probabilità. Con 3 o più server, basta impostare i valori iniziali su 0/1/2 con incrementi di 3, per quattro è 0/1/2/3 con incrementi di 4, ecc ...

dettagli sulle impostazioni lato server qui:

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

In questo modo è sufficiente ripristinare l'auto_increment una volta al tavolo per server, e poi si prenderanno cura del problema unicità automaticamente.

+0

Questo non ha senso per me. Sto copiando le righe all'interno di un database –

+0

È possibile impostare i diversi incrementi su tabelle diverse all'interno dello stesso DB. Se la tabella A inizia da 0 e incrementa per2, e la tabella B inizia da 1 e incrementa di 2, quindi puoi copiare le righe avanti e indietro a piacimento senza preoccuparti delle collisioni di chiavi –

+0

Nel mio caso gli ID stanno generando solo nella tabella A. –

3

Non c'è un modo semplice per aggirare il comportamento predefinito dell'attributo AUTO_INCREMENT in MySQL, e anche se si trova un modo, io non mi consiglia di farlo, dal momento che è il modo migliore per incorrere in problemi nel breve termine. I valori AUTO_INCREMENT non devono essere modificati o resettati in un ambiente di produzione.

Una possibile soluzione al tuo problema potrebbe essere quella di denormalizzare un po 'il tuo modello. L'idea è di spostare il campo AUTO_INCREMENT in una tabella laterale in cui non è necessario copiare o eliminare righe. Tutto quello che devi fare è ottenere un nuovo valore id da questa tabella laterale quando crei un nuovo elemento e mantieni il valore id esistente quando copi le righe da una tabella a un'altra.

Per raggiungere questo obiettivo verrà utilizzato un trigger che creerà un nuovo ID per noi e assegnarlo al nostro record di articolo. Il campo id della tabella degli articoli deve essere annullabile affinché funzioni, quindi dobbiamo sostituire la chiave primaria con un indice univoco.

Questo cambiamento modello sarebbe completamente trasparente per l'applicazione, in modo da non avrebbe alcun cambiamento per rendere nel codice dell'applicazione.

Ecco alcuni esempi di script. Diciamo che abbiamo due tavoli voce nel database con alcune righe comuni, e alcune righe che devono essere spostati dal primo tavolo a seconda tabella:

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

Se proviamo a spostare alcuni dati da una tabella all'altra e quindi riavviare il server, riscontreremo il problema del ripristino dei valori AUTO_INCREMENT. Quindi dovremo modificare leggermente il nostro modello come segue:

New model with side table

si procederà in più fasi per migrare il nostro modello di dati. Le istruzioni DDL nei seguenti script di migrazione sono state generate utilizzando l'IDE neXtep Designer.

  • Per prima cosa creare una nuova tabella item_keys che conterrà il campo 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); 
  • Ma prima di attivare l'attributo AUTO_INCREMENT, dobbiamo inserire gli ID esistenti nella nostra nuova tabella:
 
-- 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 
; 
  • Ora possiamo attivare l'attributo AUTO_INCREMENT, e inizializzare il suo valore per inserti futuri:
 
-- 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; 
  • Poi possiamo modificare le tabelle item1 e item2 per sostituire la chiave primaria per un indice univoco, e riferimento alla chiave primaria della tabella 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) 
; 
  • Infine, non ci resta che creare i trigger che gestiranno la creazione ids per noi:
 
-- 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; 
|; 

Ora possiamo spostare i dati da un tavolo all'altro, mantenendo inalterate le ids, e se riavviamo il server, il valore AUTO_INCREMENT nello item_keys rimarrà lo stesso.

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

Grazie per un consiglio. , ma questo approccio causerà alcuni problemi con perfomance –

+0

@Andrew, qual è il livello di prestazioni necessario per quale tipo di operazione (inserimento di elementi, inserimento di massa di elementi, eliminazione di articoli, selezione di articoli)? Puoi fornire delle metriche? eseguire un punto di riferimento in modo da poter dire che questo non è abbastanza buono? –

+0

@Andrew, vorrei capire se hai trovato questa soluzione troppo complicata rispetto a Inserimento di record temporanei, ma non capisco l'argomento della performance. –