¿ALTERAR LA MESA sin bloquear la mesa?

107

Cuando se realiza una instrucción ALTER TABLE en MySQL, toda la tabla está bloqueada para lectura (permitiendo lecturas simultáneas, pero prohibiendo escrituras simultáneas) durante la duración de la declaración. Si se trata de una tabla grande, las declaraciones INSERT o UPDATE podrían bloquearse durante muuuucho tiempo. ¿Hay alguna manera de hacer un "cambio en caliente", como agregar una columna de tal manera que la tabla aún sea actualizable durante todo el proceso?

Principalmente, estoy interesado en una solución para MySQL, pero estaría interesado en otros RDBMS si MySQL no puede hacerlo.

Para aclarar, mi propósito es simplemente evitar el tiempo de inactividad cuando una nueva función que requiere una columna de tabla adicional se envía a producción. Cualquier esquema de base será cambiar con el tiempo, que es sólo un hecho de la vida. No veo por qué deberíamos aceptar que estos cambios deben resultar inevitablemente en tiempo de inactividad; eso es solo débil.

Daniel
fuente
2
¿Me pregunto cuántas veces alterará la tabla?
Allain Lalonde
1
En mi humilde opinión, los cambios en el esquema de la base de datos están asociados con versiones completamente nuevas; no se implementan esporádicamente como lo hacen otros cambios. Es inevitablemente un gran problema.
dkretz
9
@AllainLalonde: más de 0 veces hace que esta pregunta sea legítima, especialmente si el tiempo de inactividad en su sistema costaría vidas o mucho dinero. Y en cualquier caso, a veces aparecen nuevos requisitos de software.
Nathan Long

Respuestas:

60

La única otra opción es hacer manualmente lo que muchos sistemas RDBMS hacen de todos modos ...
- Crear una nueva tabla

A continuación, puede copiar el contenido de la tabla anterior en un fragmento a la vez. Aunque siempre tenga cuidado con cualquier INSERT / UPDATE / DELETE en la tabla de origen. (Podría ser administrado por un disparador. Aunque esto causaría una desaceleración, no es un bloqueo ...)

Una vez terminado, cambie el nombre de la tabla fuente, luego cambie el nombre de la nueva tabla. Preferiblemente en una transacción.

Una vez terminado, vuelva a compilar los procedimientos almacenados, etc. que utilicen esa tabla. Es probable que los planes de ejecución ya no sean válidos.

EDITAR:

Se han hecho algunos comentarios acerca de que esta limitación es un poco pobre. Así que pensé en darle una nueva perspectiva para mostrar por qué es así ...

  • Agregar un nuevo campo es como cambiar un campo en cada fila.
  • Los bloqueos de campo serían mucho más difíciles que los bloqueos de fila, sin importar los bloqueos de mesa.

  • De hecho, estás cambiando la estructura física del disco, cada registro se mueve.
  • Esto realmente es como una ACTUALIZACIÓN en toda la mesa, pero con más impacto ...
MatBailie
fuente
2
Y tenga un plan de prueba completo antes de cambiar. Si falla, comience de nuevo.
dkretz
2
Gestionar la sincronización a través de disparadores fue una buena idea. He estado usando MySQL durante tanto tiempo que sigo olvidando que tienen activadores ahora. He usado esta técnica y ahora tengo un script funcional de hot-alter. Con barra de progreso. Y funciona con MyISAM. La vida es buena.
Daniel
2
+1 Esto es literalmente lo que hace SQL Enterprise Manager entre bastidores cuando realiza ciertos tipos de cambios en la tabla en la interfaz de usuario. En SQL 2008, en realidad agregaron una advertencia para que el usuario SABE que está realizando esta acción drástica.
BradC
2
No ha mencionado nada sobre claves externas que hagan referencia a las tablas que se están modificando. ¿No sería eso un problema?
Rafay
2
@MohammadRafayAleem - Y los campos AUTOINCREMENT, las vistas, los disparadores, etc. Pero aun así, el enfoque sigue siendo viable.
MatBailie
42

Percona crea una herramienta llamada pt-online-schema-change que permite hacer esto.

Básicamente, hace una copia de la tabla y modifica la nueva tabla. Para mantener la nueva tabla sincronizada con la original, utiliza activadores para actualizar. Esto permite acceder a la tabla original mientras se prepara la nueva tabla en segundo plano.

Esto es similar al método sugerido por Dems anteriormente, pero lo hace de manera automatizada.

Algunas de sus herramientas tienen una curva de aprendizaje, es decir, se conectan a la base de datos, pero una vez que las tienes, son excelentes herramientas.

Ex:

pt-online-schema-change --alter "ADD COLUMN c1 INT" D=db,t=numbers_are_friends
SeanDowney
fuente
Parece que el vínculo está roto. Encontré que este enlace funciona.
Noam Ben Ari
25

Esta pregunta de 2009. Ahora MySQL ofrece una solución:

DDL en línea (lenguaje de definición de datos)

Una función que mejora el rendimiento, la simultaneidad y la disponibilidad de las tablas InnoDB durante las operaciones DDL (principalmente ALTER TABLE). Consulte la Sección 14.11, “InnoDB y DDL en línea” para obtener más detalles.

Los detalles varían según el tipo de operación. En algunos casos, la tabla se puede modificar simultáneamente mientras ALTER TABLE está en progreso. Es posible que la operación se pueda realizar sin hacer una copia de la tabla o utilizando un tipo de copia de tabla especialmente optimizado. El uso del espacio está controlado por la opción de configuración innodb_online_alter_log_max_size.

Le permite ajustar el equilibrio entre rendimiento y simultaneidad durante la operación DDL, eligiendo si bloquear el acceso a la tabla por completo (LOCK = cláusula EXCLUSIVA), permitir consultas pero no DML (cláusula LOCK = SHARED) o permitir consulta completa y DML acceso a la tabla (cláusula LOCK = NONE). Cuando omite la cláusula LOCK o especifica LOCK = DEFAULT, MySQL permite tanta simultaneidad como sea posible según el tipo de operación.

Realizar cambios en el lugar donde sea posible, en lugar de crear una nueva copia de la tabla, evita aumentos temporales en el uso del espacio en disco y la sobrecarga de E / S asociada con la copia de la tabla y la reconstrucción de índices secundarios.

consulte el Manual de referencia de MySQL 5.6 -> InnoDB y DDL en línea para obtener más información.

Parece que el DDL online también está disponible en MariaDB

Alternativamente, puede usar ALTER ONLINE TABLE para asegurarse de que su ALTER TABLE no bloquee operaciones concurrentes (no se bloquee). Es equivalente a LOCK = NONE.

MariaDB KB sobre ALTER TABLE

Ivanov
fuente
3
Es una pena que no haya otra forma que los votos para hacer flotar esto a la parte superior, dado que en su mayoría niega todas las otras respuestas simplemente porque ya no hacen referencia a la versión actual de MySQL.
Burhan Ali
14

Recomiendo Postgres si esa es una opción. Con postgres, esencialmente no hay tiempo de inactividad con los siguientes procedimientos:

Otra gran característica es que la mayoría de las declaraciones DDL son transaccionales, por lo que puede hacer una migración completa dentro de una transacción SQL y, si algo sale mal, todo se revierte.

Escribí esto hace un poco, tal vez pueda arrojar algo más de información sobre los otros méritos.

mikelikespie
fuente
6
Postgres aún crea un bloqueo exclusivo en el alter, evitando que otros lean de esa tabla.
clofresh
5
No estoy de acuerdo con el bit "esencialmente sin tiempo de inactividad". Como dijo clofresh, ALTER TABLE agarra un candado exclusivo en la tabla que bloquea todas las lecturas y escrituras simultáneas. En mi experiencia, para las tablas activas la mayoría de las veces ni siquiera obtendrá el bloqueo (ALTER TABLE morirá de hambre). Y con las transacciones, puede terminar fácilmente con puntos muertos si no tiene mucho cuidado. Por eso, ahora siempre establezco tiempos de inactividad al modificar las tablas existentes en Postgres.
Pankrat
1
una explicación más detallada: dba.stackexchange.com/questions/27153/… menciona las implicaciones del bloqueo exclusivo y algunas formas de solucionarlo
John Douthat
4
Sí, alterar una tabla en postgres obtiene un bloqueo exclusivo, pero debido a que la operación en sí se completa en milisegundos, esto es prácticamente irrelevante en la mayoría de los casos. Personalmente, he agregado columnas a tablas de cien millones de filas en medio del día hábil sin tiempo de inactividad resultante.
Noah Yetter
2
@cobbzilla Sí, DROP COLUMN es igual de rápido. Debajo del capó, lo que básicamente hace es marcar la columna como oculta. Los valores que existían en esa columna antes de que se descartaran todavía están en los archivos de datos (y son visibles para otras transacciones), y permanecerán así a menos que y hasta que realice una VACÍO LLENO.
Noah Yetter
7

Dado que preguntó acerca de otras bases de datos, aquí hay información sobre Oracle.

Agregar una columna NULL a una tabla de Oracle es una operación muy rápida ya que solo actualiza el diccionario de datos. Esto mantiene un candado exclusivo en la mesa durante un período de tiempo muy corto. Sin embargo, invalidará cualquier procedimiento almacenado dependiente, vistas, disparadores, etc. Estos serán recompilados automáticamente.

Desde allí, si es necesario, puede crear un índice utilizando la cláusula ONLINE. Nuevamente, solo bloqueos de diccionario de datos muy cortos. Leerá toda la tabla buscando cosas para indexar, pero no bloquea a nadie mientras hace esto.

Si necesita agregar una clave externa, puede hacerlo y hacer que Oracle confíe en que los datos son correctos. De lo contrario, debe leer toda la tabla y validar todos los valores que pueden ser lentos (primero cree su índice).

Si necesita poner un valor predeterminado o calculado en cada fila de la nueva columna, deberá ejecutar una actualización masiva o quizás un pequeño programa de utilidad que complete los nuevos datos. Esto puede ser lento, especialmente si las filas se hacen mucho más grandes y ya no caben en sus bloques. El bloqueo se puede gestionar durante este proceso. Dado que el antiguo versino de su aplicación, que aún se está ejecutando, no conoce esta columna, es posible que necesite un disparador furtivo o especificar un valor predeterminado.

Desde allí, puede hacer un cambio en sus servidores de aplicaciones a la nueva versión del código y seguirá funcionando. Suelta tu gatillo furtivo.

Alternativamente, puede usar DBMS_REDEFINITION, que es una caja negra diseñada para hacer este tipo de cosas.

Todo esto es tan molesto de probar, etc. que solo tenemos una interrupción el domingo por la mañana cada vez que lanzamos una versión principal.

WW.
fuente
3

Si no puede permitirse el tiempo de inactividad de su base de datos cuando realiza actualizaciones de aplicaciones, debería considerar mantener un clúster de dos nodos para una alta disponibilidad. Con una configuración de replicación simple, podría hacer cambios estructurales casi completamente en línea como el que sugiere:

  • esperar a que todos los cambios se repliquen en un esclavo pasivo
  • cambiar el esclavo pasivo para que sea el maestro activo
  • hacer los cambios estructurales al viejo maestro
  • replicar los cambios desde el nuevo maestro al antiguo maestro
  • realizar el intercambio maestro nuevamente y la implementación de la nueva aplicación simultáneamente

No siempre es fácil, pero funciona, ¡normalmente con 0 tiempos de inactividad! El segundo nodo no tiene por qué ser solo pasivo, se puede utilizar para realizar pruebas, realizar estadísticas o como nodo de reserva. Si no tiene infraestructura, la replicación se puede configurar en una sola máquina (con dos instancias de MySQL).

jynus
fuente
1
¿El antiguo maestro está fuera del clúster o dentro del clúster?
John Chornelius
2

No Si está utilizando tablas MyISAM, según tengo entendido, solo hacen bloqueos de tabla; no hay bloqueos de registro, solo intentan mantener todo hiperrápido a través de la simplicidad. (Otras tablas MySQL operan de manera diferente). En cualquier caso, puede copiar la tabla a otra tabla, modificarla y luego cambiarlas, actualizándolas para detectar diferencias.

Esta es una alteración tan masiva que dudo que ningún DBMS la admita. Se considera un beneficio poder hacerlo con los datos de la tabla en primer lugar.

dkretz
fuente
InnoDB usa bloqueos de fila - dev.mysql.com/doc/refman/5.0/en/internal-locking.html
Eran Galperin
Sí, MySQL es la aberración. Por eso fui específico sobre las tablas "estándar".
dkretz
Escribiste: las tablas estándar de MySQL solo hacen bloqueos de tablas, lo cual es incorrecto.
Eran Galperin
¿Cómo interpreta esto sobre las tablas MyISAM (es decir, el estándar MySQL) de la página que citó? "MySQL usa bloqueo a nivel de tabla para MyISAM y MEMORY, bloqueo a nivel de página para tablas BDB y bloqueo a nivel de fila para tablas InnoDB".
dkretz
algunos motores de almacenamiento usan bloqueo de nivel de fila y algunos usan bloqueo de nivel de tabla. No hay un motor de almacenamiento estándar (tal vez te refieres al predeterminado en phpMyAdmin ...)
Eran Galperin
2

Solución temporal...

Otra solución podría ser agregar otra tabla con la clave principal de la tabla original, junto con su nueva columna.

Complete su clave principal en la nueva tabla y complete los valores para la nueva columna en su nueva tabla, y modifique su consulta para unir esta tabla para seleccionar operaciones y también necesita insertar, actualizar por separado para este valor de columna.

Cuando pueda tener tiempo de inactividad, puede alterar la tabla original, modificar sus consultas DML y eliminar su nueva tabla creada anteriormente

De lo contrario, puede optar por el método de agrupación en clústeres, la replicación, la herramienta pt-online-schema de percona

Balasundaram
fuente
1

Usando el complemento Innodb, las declaraciones ALTER TABLE que solo agregan o eliminan índices secundarios se pueden hacer "rápidamente", es decir, sin reconstruir la tabla.

Sin embargo, en general, en MySQL, cualquier ALTER TABLE implica reconstruir la tabla completa, lo que puede llevar mucho tiempo (es decir, si la tabla tiene una cantidad útil de datos).

Realmente necesita diseñar su aplicación para que las declaraciones ALTER TABLE no tengan que realizarse con regularidad; ciertamente no desea que se realice ALTER TABLE durante la ejecución normal de la aplicación a menos que esté preparado para esperar o esté alterando tablas pequeñas.

MarkR
fuente
1

Recomendaría uno de dos enfoques:

  1. Diseñe las tablas de su base de datos teniendo en cuenta los posibles cambios. Por ejemplo, he trabajado con Content Management Systems, que cambian los campos de datos en el contenido con regularidad. En lugar de construir la estructura de la base de datos física para que coincida con los requisitos de campo iniciales del CMS, es mucho mejor construir una estructura flexible. En este caso, use un campo de texto de blob (varchar (max) por ejemplo) para contener datos XML flexibles. Esto hace que los cambios estructurales sean menos frecuentes. Los cambios estructurales pueden ser costosos, por lo que también hay un beneficio en el costo aquí.

  2. Tenga tiempo de mantenimiento del sistema. O el sistema se desconecta durante los cambios (mensuales, etc.) y los cambios se programan durante el momento del día con menos tráfico (3-5 a. M., Por ejemplo). Los cambios se organizan antes del lanzamiento de la producción, por lo que tendrá una buena estimación de la ventana fija del tiempo de inactividad.

2a. Tenga servidores redundantes, de modo que cuando el sistema tenga tiempo de inactividad, todo el sitio no se caiga. Esto le permitiría "desplegar" sus actualizaciones de forma escalonada, sin que todo el sitio se caiga.

Las opciones 2 y 2a pueden no ser viables; tienden a ser solo para sitios / operaciones más grandes. Sin embargo, son opciones válidas y personalmente he utilizado todas las opciones presentadas aquí.

pearcewg
fuente
1

Si alguien todavía está leyendo esto o viene aquí, este es el gran beneficio de usar un sistema de base de datos NoSQL como mongodb. Tuve el mismo problema al tratar de alterar la tabla para agregar columnas para características adicionales o índices en una tabla grande con millones de filas y escrituras altas. Terminaría bloqueándose durante mucho tiempo, por lo que hacer esto en la base de datos LIVE frustraría a nuestros usuarios. En mesas pequeñas puede salirse con la suya.

Odio el hecho de que tengamos que "diseñar nuestras tablas para evitar alterarlas". Simplemente no creo que eso funcione en el mundo actual de los sitios web. No puede predecir cómo la gente usará su software, por eso cambia rápidamente las cosas en función de los comentarios de los usuarios. Con mongodb, puede agregar "columnas" a voluntad sin tiempo de inactividad. Realmente ni siquiera los agrega, simplemente inserta datos con nuevas columnas y lo hace automáticamente.

Vale la pena echarle un vistazo: www.mongodb.com

Brian Gruber
fuente
2
MySQL todavía se usa en muchos sistemas, por lo que la pregunta es realmente cómo lograr un cambio de esquema en SQL RDBMS, aunque también soy un ferviente partidario de NoSQL.
Alexy
1

En general, la respuesta será "No". Está cambiando la estructura de la tabla que potencialmente requerirá muchas actualizaciones "y definitivamente estoy de acuerdo con eso. Si espera hacer esto con frecuencia, entonces ofreceré una alternativa a las columnas" ficticias "; use VIEWs en su lugar de tablas para obtener SELECTdatos. IIRC, cambiar la definición de una vista es relativamente ligero y la indirección a través de una vista se realiza cuando se compila el plan de consulta. El costo es que tendría que agregar la columna a una nueva tabla y hacer que ver JOINen la columna.

Por supuesto, esto solo funciona si puede usar claves externas para realizar una cascada de eliminaciones y todo eso. La otra ventaja es que puede crear una nueva tabla que contenga una combinación de los datos y apuntar la vista hacia ella sin perturbar el uso del cliente.

Solo un pensamiento.

D.Shawley
fuente
1

La diferencia entre Postgres y MySQL a este respecto es que en Postgres no vuelve a crear una tabla, sino que modifica el diccionario de datos que es similar a Oracle. Por lo tanto, la operación es rápida, mientras que aún requiere asignar un bloqueo de tabla DDL exclusivo por un tiempo muy corto, como lo indicaron otros.

En MySQL, la operación copiará los datos a una nueva tabla mientras bloquea las transacciones, lo que ha sido el principal problema para los DBA de MySQL antes de la versión 5.6.

La buena noticia es que desde el lanzamiento de MySQL 5.6, la restricción se ha eliminado en su mayoría y ahora puede disfrutar del verdadero poder de MYSQL DB.

Dmitriy Royzenberg
fuente
3
Parece que estaba intentando vincular a una referencia relacionada con un cambio en MySql 5.6, pero no funcionó. Inténtalo de nuevo.
dg99
0

Las columnas ficticias son una buena idea si puede predecir su tipo (y hacerlas anulables). Compruebe cómo su motor de almacenamiento maneja los nulos.

MyISAM bloqueará todo si incluso menciona el nombre de una mesa de pasada, por teléfono, en el aeropuerto. Simplemente hace eso ...

Dicho esto, las cerraduras no son realmente tan importantes; siempre que no intente agregar un valor predeterminado para la nueva columna a cada fila, pero déjelo como nulo, y su motor de almacenamiento sea lo suficientemente inteligente como para no escribirlo, debería estar bien con un bloqueo que es solo retenido el tiempo suficiente para actualizar los metadatos. Si intenta escribir un nuevo valor, bueno, está hecho un brindis.

SquareCog
fuente
1
Intenté agregar una columna NULL a una tabla InnoDB y tuve que reconstruir toda la tabla; no es una simple operación de "actualizar los metadatos".
Daniel
Creo que la idea era incluir columnas adicionales que aceptaran valores NULL en la base de datos cuando se diseñó, de modo que si se requiere una nueva función, se puede "agregar" una nueva columna simplemente comenzando a usarla. No tendrá un nombre agradable, pero si el tipo de datos se eligió / predijo correctamente, debería funcionar.
supercat
0

TokuDB puede agregar / eliminar columnas y agregar índices "calientes", la tabla está completamente disponible durante todo el proceso. Está disponible a través de www.tokutek.com

tmcallaghan
fuente
-6

Realmente no.

ESTÁS alterando la estructura subyacente de la tabla, después de todo, y esa es un poco de información que es bastante importante para el sistema subyacente. También (probablemente) esté moviendo gran parte de los datos en el disco.

Si planea hacer esto mucho, es mejor que simplemente rellene la tabla con columnas "ficticias" que están disponibles para uso futuro.

Will Hartung
fuente
3
Rellenar una tabla con columnas falsas parece ser una muy mala idea.
Jost