Cómo agregar una columna a una tabla grande en MySQL

13

Soy un desarrollador de PHP, así que no seas estricto. Tengo una gran mesa ~ 5.5 gb volcado. Nuestro primer ministro decidió crear una nueva columna para realizar una nueva función. La tabla es InnoDB, así que lo intenté:

  1. Alterar la mesa en la pantalla con bloqueo de mesa. Tomó ~ 30 horas y nada. Entonces lo detuve. Primero cometí un error porque no terminé todas las transacciones, pero la segunda vez no fue multilock. El estado era copy to tmp table.

  2. Como también necesito aplicar particiones para esta tabla, decidimos hacer un volcado, cambiar el nombre y crear una tabla con el mismo nombre y una nueva estructura. Pero dump está haciendo una copia estricta (al menos no encontré otra cosa). Así que agregué para volcar una nueva columna sedy consultarla. Pero comenzaron algunos errores extraños. Creo que fue causado por charset. La tabla en utf-8 y el archivo se convirtieron en us-ascii después sed. Entonces obtuve errores (comando desconocido '\' ') en el 30% de los datos. Entonces esta también es una mala manera.

¿Cuáles son otras opciones para lograr esto y acelerar el rendimiento (puedo hacerlo con el script php, pero tomará años)? ¿Cuál será el rendimiento INSERT SELECTen este caso?

Gracias por cualquier avance.

ineersa
fuente

Respuestas:

12

Use MySQL Workbench . Puede hacer clic con el botón derecho en una tabla y seleccionar "Enviar al editor de SQL" -> "Crear declaración". De esta forma, ninguna tabla de "propiedades" se olvidará de agregar (incluyendo CHARSETo COLLATE).
Con esta gran cantidad de datos, recomendaría limpiar la tabla o la estructura de datos que usa (un buen DBA es útil). Si no es posible:

  • cambie el nombre de la tabla ( ALTER) y cree una nueva con el CREATEscript que obtiene de Workbench. También puede extender esa consulta con el nuevo campo que necesita
  • CARGAR A GRANEL los datos de la tabla anterior a la nueva:
    SET FOREIGN_KEY_CHECKS = 0;
    SET UNIQUE_CHECKS = 0;
    SET AUTOCOMMIT = 0;
    INSERT INTO new_table (fieldA, fieldB, fieldC, ..., fieldN)
       SELECT fieldA, fieldB, fieldC, ..., fieldN
       FROM old_table
    SET UNIQUE_CHECKS = 1;
    SET FOREIGN_KEY_CHECKS = 1;
    COMMIT;

    De esta manera, evita la indexación / etc para ejecutar registro por registro. La "actualización" de la tabla seguirá siendo lenta (ya que la cantidad de datos es enorme), pero esta es la forma más rápida que se me ocurre.

    EDITAR: Lea este artículo para obtener detalles sobre los comandos utilizados en la consulta de muestra anterior;)

fuente
Mis opciones están bien. Y obtuve SET NAMES utf8y. COLLATIONPero meh idk por qué el 30% de los datos se corrompió después sed. Creo que la carga masiva será la más rápida, pero tal vez exista algo más que me estoy perdiendo. Gracias Mark
ineersa
1
La corrupción de datos de @ineersa puede tener muchas razones: por ejemplo, abrió el archivo con un editor que no admite todos los caracteres y lo guardó. O bien, la forma en que intenta importar desde el volcado corrompe los datos (tiene errores y no puede leer el archivo correctamente). O bien, el mismo tipo puede identificar parte de algunos datos como una expresión (por ejemplo, "james \ robin" == "\ r" como expresión) o comando, etc. Es por eso que nunca recomiendo usar dump, ni siquiera con la herramienta de volcado de datos binarios solo, ni siquiera con dev.mysql.com/doc/refman/5.6/en/mysqldump.html (o BCP para MS SQL Server).
Sí, intenté con hex-blob. No ayuda. También justo después de usar sed mysql identifica \ 'como comando en algunos nombres (no en todos). Eso es extraño y con errores. Intentará carga masiva esta noche. Espero que se haga al menos en 10-15 hrs.
ineersa
@ineersa espero que lo haga. también puede intentar agregar solo una parte de los datos, digamos el 10% para ver cuánto tiempo lleva, y tener una estimación de toda la transacción. Sin embargo, será una estimación muy aproximada, las cosas pueden ir lento si los cachés / memoria / lo que sea se llene / sobrecargue.
1
Gracias Mark Funcionó genial. Incluso más rápido que restaurar desde el volcado. Tomó ~ 5 hrs.
ineersa
5

Su idea sed es un método decente, pero sin los errores o el comando que ejecutó, no podemos ayudarlo.

Sin embargo, un método bien conocido para realizar cambios en línea en tablas grandes es pt-online-schema-change . La descripción simplista de lo que hace esta herramienta se copia de la documentación:

pt-online-schema-change funciona creando una copia vacía de la tabla para modificarla, modificándola como se desee y luego copiando las filas de la tabla original en la nueva tabla. Cuando se completa la copia, aleja la tabla original y la reemplaza por la nueva. Por defecto, también descarta la tabla original.

Este método también puede tardar un tiempo en completarse, pero durante el proceso la tabla original será completamente utilizable.

Derek Downey
fuente
Intentaré cargar a granel más tarde esta noche. Si no funciona probablemente necesitará esta herramienta. Los errores son causados ​​por la inserción de algunos símbolos después de usar sed como comandos. Por ejemplo 'D\'agostini'causará error unknown command '\''. Pero no siempre, como en el 30% de los casos. Eso es extraño y con errores. Lo mismo ocurre incluso con los volcados de blobs hexadecimales. Gracias Derek
ineersa
4

alter table add column, algorithm=inplace, lock=none alterará una tabla MySQL 5.6 sin copiar la tabla y sin impacto de bloqueo.

Apenas probé esto ayer, la masa insertó 70K filas en una tabla de partición de 280K fila 7, 10K filas en cada partición, con 5 segundos de descanso en el medio para permitir otro rendimiento.

Comenzó las inserciones masivas, luego, en una sesión separada, comencé la alterdeclaración en línea anterior en MySQL Workbench, alterterminó antes de las inserciones, se agregaron dos nuevas columnas y no hubo filas como resultado de la modificación, lo que significa que MySQL no copió ninguna fila.

SAK
fuente
1
¿Por qué esta respuesta no obtiene más votos ?, ¿no está funcionando?
fguillen
1

Actualmente, la mejor opción para alterar tablas enormes es probablemente https://github.com/github/gh-ost

gh-ost es una solución de migración de esquemas en línea sin disparadores para MySQL. Es comprobable y proporciona capacidad de pausa, control dinámico / reconfiguración, auditoría y muchas ventajas operativas.

gh-ost produce una carga de trabajo ligera en el maestro durante la migración, desacoplada de la carga de trabajo existente en la tabla migrada.

Ha sido diseñado en base a años de experiencia con soluciones existentes y cambia el paradigma de las migraciones de tablas.

iJanki
fuente
1

Creo que Mydumper / Myloader es una buena herramienta para operaciones como esta: está mejorando cada día. Puede utilizar sus CPU y cargar datos en paralelo: http://www.percona.com/blog/2014/03/10/new-mydumper-0-6-1-release-offers-several-performance-and- características de usabilidad /

He logrado cargar cientos de gigabytes de tablas MySQL en horas.

Ahora, cuando se trata de agregar una nueva columna, es complicado ya que MySQL copia toda la tabla en el TMPárea de memoria con ALTER TABLE...Aunque MySQL 5.6 dice que puede hacer cambios en el esquema en línea, no he logrado hacerlo en línea para tablas masivas sin bloqueo contención hasta ahora.

Kubilay
fuente
-2

Acabo de tener el mismo problema. Una pequeña solución:

CREATE TABLE new_table SELECT * FROM oldtable;

BORRAR DE new_table

ALTER TABLE new_table ADD COLUMN new_column int (11);

INSERTAR EN new_table select *, 0 de old_table

soltar tabla old_table; renombrar tabla new_table TO old_table;

AirCoder
fuente
¿Por qué no simplemente agregar una cláusula where a la instrucción create table para que no seleccione ningún dato? También truncar la tabla sería más eficiente que eliminar los datos
Joe W
por qué eliminar, cuando tiene que insertar más tarde, nuevamente. Puede definir default = 0 en AGREGAR COLUMNA.
user195280