2010-11-08 12 views
5

Tengo muchas tablas y por alguna razón necesito ajustar el valor de incremento automático para estas tablas al inicio de la aplicación.Restablecimiento automático lento reinicio

Trato de hacer esto:

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

mysql> alter table item auto_increment=1097972232; 

En otra sesión:

[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 está empezando a reconstruir la mesa! ¿Por qué MySQL necesita hacerlo? ¿Cómo puedo evitar reconstruir tablas enormes al ajustar el valor de auto_increment?

MySQL 5.0, InnoDB. definición
Tabla:

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; 

acerca de por qué tengo que hacer esto. Para resumir, quiero solucionar el problema de mysql innodb sobre restablecer el valor de auto_increment en el reinicio del servidor. A veces copiamos las filas de nuestras tablas a otras tablas y debemos mantener el ID de las filas sin cambios. Cuando agregamos una fila (con id = 1 por ejemplo) a table1, copiamos row a table2, eliminamos row de table1 y reiniciamos MySQL, luego cuando creamos una nueva fila en table1 esta fila también obtendrá id = 1. Entonces, si tendremos que copiar la fila a la tabla 2, obtenemos una violación de restricción única. Ya tenemos mucho código y será difícil volver a escribirlo todo. Ajustar el valor de autoincrement parece la forma más fácil de solucionar este problema.

Agregado:

MySQL 5.5 - todos lo mismo :(

+0

MySQL 5.0 no es una versión, es una familia completa de versiones. Proporcione los tres dígitos de la versión. Si no lo sabe, 'muestre variables como '% version%' ' – derobert

+3

Además, * Necesito ajustar los valores de incremento automático ... al inicio de la aplicación * me parece * que lo está haciendo mal *. – derobert

+2

¿Por qué en cada foro hay un chico que sabe exactamente que lo estoy haciendo mal? :) –

Respuesta

12

simplemente añadir un registro temporal que ha deseado auto_increment_id-1 a cada mesa, y eliminar el registro después de eso, rápida y fácil, pero probablemente demasiado sucia

ejemplo:

insert into item set id=1097972232-1;

después de la ejecución, el próximo auto_increment será 1097972232, que es lo que desea

esto puede evitar la lentitud

+0

Y luego probablemente 'eliminar del elemento donde id = 1097972232-1' para que no tenga una fila de basura. Pero esto funciona He utilizado este truco para permitir que las inserciones activas continúen dejando suficiente espacio para mover algunas filas de otra (como una copia de seguridad, etc.). –

+0

¡Guau! Muy buena idea. :) ¡Muchas gracias! Tendré algunos problemas con las claves externas, pero estos son problemas menores. –

+1

En realidad, insertar + revertir es suficiente. –

-1

No es:

ALTER TABLE item AUTO_INCREMENT=1; 

Source

+0

Cuando ejecuto este comando MySQL reconstruye la tabla y toma mucho tiempo ... –

+0

¿Qué quiere decir "reconstruir" la tabla? Simplemente cambia el valor predeterminado de auto_increment. ¿Su mesa ya está "llena"? ¿Estás cambiando por un incremento ya utilizado? ¿Estás usando un índice de varias columnas en esta tabla? – Shikiryu

+0

Agregué información adicional a la publicación principal. –

6

Este es un documentado? "característica" de MySQL:

Si utiliza cualquier opción para ALTERAR TABLA que no sea RENOMBRAR, MySQL siempre crea una tabla temporal, incluso si no es necesario copiar los datos estrictamente (como cuando cambia el nombre de una columna). Para las tablas MyISAM, puede acelerar la operación de recreación del índice (que es la parte más lenta del proceso de alteración) estableciendo la variable del sistema myisam_sort_buffer_size en un valor alto.

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

MySQL 5.1 y 5.5 de apoyo unas cuantas operaciones de tabla alter w/o una tabla temporal, pero cambiando el AUTO_INCREMENT no está documentada a ser uno de esos.

¿Por qué necesita cambiar el valor de auto_increment, de todos modos? Esto no es algo que deberías hacer rutinariamente.

+0

Gracias por su asesoramiento. Mañana comprobaré este comportamiento en 5.1 y 5.5. Actualicé la primera publicación para responder por qué tengo que hacerlo. –

+0

Activado 5.5. De todos modos :( –

1

Si necesita mantener identificaciones únicas entre dos o más servidores, no use este método alter table para restablecer el auto_increment cada vez. Sería más fácil cambiar el incremento del incremento para que cada servidor genere ID únicos sin intervención. Para dos servidores, configura uno para que comience en 0 y uno para que comience en 1, con un incremento de 2; luego uno generará ID pares, el otro generará probabilidades. Con 3 o más servidores, simplemente establece los valores iniciales en 0/1/2 con incrementos de 3, para cuatro es 0/1/2/3 con inc de 4, etc ...

detalles sobre la configuración del lado del servidor aquí:

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

De esta manera es suficiente para restablecer el AUTO_INCREMENT una vez por mesa por servidor, y después ellos se ocuparán del problema singularidad automáticamente.

+0

Esto no tiene sentido para mí. Estoy copiando filas dentro de una base de datos. –

+0

También puede establecer los diferentes incrementos en diferentes tablas dentro de la misma base de datos. Si la tabla A comienza en 0 e incrementa en 2, y la tabla B comienza en 1 y se incrementa en 2, luego puede copiar las filas hacia adelante y hacia atrás sin tener que preocuparse por las colisiones de teclas. –

+0

En mi caso, los IDs se generan solo en la tabla A. –

3

No hay una forma sencilla de evitar el comportamiento predeterminado de atributo AUTO_INCREMENT en MySQL, e incluso si encuentra una forma, no lo recomendaría, ya que es la mejor manera de tener problemas en el término corto. Los valores AUTO_INCREMENT no están destinados a ajustarse o reiniciarse en un entorno de producción.

Una posible solución a su problema podría ser desnormalizar un poco su modelo. La idea es mover el campo AUTO_INCREMENT a una tabla lateral donde no tiene que copiar o eliminar filas. Todo lo que tiene que hacer entonces es obtener un nuevo valor de identificación de esta tabla auxiliar cuando crea un nuevo elemento, y mantener el valor de identificación existente al copiar las filas de una tabla a otra.

Para lograr esto, utilizaremos un disparador que creará una nueva identificación para nosotros y la asignará a nuestro registro de artículos. El campo id de la tabla de elementos tiene que ser anulable para que esto funcione, por lo que debemos reemplazar la clave principal por un índice único.

Este cambio de modelo sería completamente transparente para su aplicación, por lo que tendría ningún cambio a realizar en el código de aplicación.

Aquí hay algunos scripts de ejemplo. Supongamos que tenemos dos tablas de artículos en nuestra base de datos, con algunas filas comunes, y algunas filas que deben ser movido de primera tabla de la segunda tabla:

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

Si tratamos de mover algunos datos de una tabla a otra y luego reinicie su servidor, nos encontraremos con el problema de AUTO_INCREMENT restablecimiento de valor. Así que vamos a modificar ligeramente nuestro modelo de la siguiente manera:

New model with side table

procederemos en varios pasos para migrar nuestro modelo de datos. Las declaraciones DDL en los siguientes scripts de migración se han generado utilizando neXtep Designer IDE.

  • En primer lugar, crear una nueva tabla item_keys que llevará a cabo el 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); 
  • Pero antes de activar el atributo AUTO_INCREMENT, hay que insertar los identificadores existentes en nuestra nueva tabla:
 
-- 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 
; 
  • Ahora podemos activar el atributo AUTO_INCREMENT, e inicializar su valor para futuras inserciones:
 
-- 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; 
  • entonces podemos alterar las item1 y item2 mesas para reemplazar la clave principal de un índice único, y referencia a la clave primaria de la tabla 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) 
; 
  • Por último, sólo tenemos que crear los factores desencadenantes que se encargará de la creación de las identificaciones para nosotros:
 
-- 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; 
|; 

Ahora podemos mover datos de una tabla a otra, manteniendo los identificadores sin cambios, y si reiniciamos el servidor, el valor AUTO_INCREMENT en item_keys se mantendrá igual.

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

Gracias por su consejo. Esta es una buena idea , pero este enfoque causará algunos problemas con el rendimiento. –

+0

@ Andrew, ¿qué nivel de rendimiento necesita para qué tipo de operación (inserción de elementos, inserción masiva de elementos, eliminación de elementos, selección de elementos)? ¿Puede proporcionar algunas métricas? ejecutar un punto de referencia para que pueda decir que esto no es lo suficientemente bueno? –

+0

@Andrew, lo entendería si usted encuentra esta solución demasiado complicada en comparación con th La inserción temporal de registros, pero no entiendo el argumento de la actuación. –

Cuestiones relacionadas