Cree un índice en una enorme tabla de producción de MySQL sin bloqueo de tabla

104

Necesito crear un índice en una tabla MySQL de ~ 5M filas. Es una tabla de producción, y temo un bloque completo de todo si ejecuto una declaración CREATE INDEX ...

¿Hay alguna forma de crear ese índice sin bloquear inserciones y selecciones?

¡Me pregunto si no tengo que detenerme, crear un índice y reiniciar mi sistema!

nocturno
fuente
1
asegúrese de que myisam_sort_buffer_size y myisam_max_sort_file_size sean lo suficientemente grandes.
Jon Black

Respuestas:

130

Actualización [2017]: MySQL 5.6 admite actualizaciones de índices en línea

https://dev.mysql.com/doc/refman/8.0/en/innodb-online-ddl-operations.html#online-ddl-index-syntax-notes

En MySQL 5.6 y superior, la tabla permanece disponible para operaciones de lectura y escritura mientras se crea o quita el índice. La instrucción CREATE INDEX o DROP INDEX solo finaliza después de que se completan todas las transacciones que acceden a la tabla, de modo que el estado inicial del índice refleja el contenido más reciente de la tabla. Anteriormente, la modificación de la tabla mientras se creaba o quitaba un índice normalmente provocaba un interbloqueo que cancelaba la instrucción INSERT, UPDATE o DELETE en la tabla.

[2015] Actualización de tablas de índices de escritura de bloques en MySQL 5.5

De la respuesta anterior:

"Si utiliza una versión superior a 5.1, los índices se crean mientras la base de datos está en línea. No se preocupe, no interrumpirá el uso del sistema de producción".

Esto es **** FALSE **** (al menos para las tablas MyISAM / InnoDB, que es lo que usa el 99,999% de la gente. Clustered Edition es diferente).

Hacer operaciones de ACTUALIZAR en una tabla se BLOQUEARÁ mientras se crea el índice. MySQL es muy, muy estúpido sobre esto (y algunas otras cosas).

Guión de prueba:

(   
  for n in {1..50}; do
    #(time mysql -uroot -e 'select  * from website_development.users where id = 41225\G'>/dev/null) 2>&1 | grep real;
    (time mysql -uroot -e 'update website_development.users set bio="" where id = 41225\G'>/dev/null) 2>&1 | grep real;
  done
) | cat -n &
PID=$!
sleep 0.05
echo "Index Update - START"
mysql -uroot website_development -e 'alter table users add index ddopsonfu (last_name, email, first_name, confirmation_token, current_sign_in_ip);'
echo "Index Update - FINISH"
sleep 0.05
kill $PID
time mysql -uroot website_development -e 'drop index ddopsonfu on users;'

Mi servidor (InnoDB):

Server version: 5.5.25a Source distribution

Salida (observe cómo la sexta operación se bloquea durante los ~ 400ms que se necesitan para finalizar la actualización del índice):

 1  real    0m0.009s
 2  real    0m0.009s
 3  real    0m0.009s
 4  real    0m0.012s
 5  real    0m0.009s
Index Update - START
Index Update - FINISH
 6  real    0m0.388s
 7  real    0m0.009s
 8  real    0m0.009s
 9  real    0m0.009s
10  real    0m0.009s
11  real    0m0.009s

Vs operaciones de lectura que no bloquean (intercambie el comentario de línea en el script):

 1  real    0m0.010s
 2  real    0m0.009s
 3  real    0m0.009s
 4  real    0m0.010s
 5  real    0m0.009s
Index Update - START
 6  real    0m0.010s
 7  real    0m0.010s
 8  real    0m0.011s
 9  real    0m0.010s
...
41  real    0m0.009s
42  real    0m0.010s
43  real    0m0.009s
Index Update - FINISH
44  real    0m0.012s
45  real    0m0.009s
46  real    0m0.009s
47  real    0m0.010s
48  real    0m0.009s

Actualización del esquema de MySQL sin tiempo de inactividad

Hasta ahora, solo conozco un método para actualizar un esquema MySql y no sufrir una interrupción de disponibilidad. Maestros circulares:

  • Master A tiene su base de datos MySQL ejecutándose
  • Ponga en servicio el maestro B y haga que replique las escrituras del maestro A (B es un esclavo de A)
  • Realice la actualización del esquema en Master B. Se retrasará durante la actualización
  • Deje que el Maestro B se ponga al día. Invariante: su cambio de esquema DEBE ser capaz de procesar comandos replicados desde un esquema de versión descendente. Los cambios de indexación califican. Las adiciones de columnas simples generalmente califican. ¿Eliminar una columna? Probablemente no.
  • Cambie ATÓMICAMENTE todos los clientes de Master A a Master B. Si desea estar seguro (confíe en mí, lo hace), debe asegurarse de que la última escritura en A se repita en B ANTESB toma su primera escritura. Si permite escrituras simultáneas a más de 2 maestros, ... comprenderá mejor la replicación de MySQL a un nivel PROFUNDO o se dirigirá a un mundo de dolor. Dolor extremo. ¿Tiene una columna que es AUTOINCREMENT ??? estás jodido (a menos que uses números pares en un maestro y probabilidades en el otro). NO confíe en que la replicación de MySQL "hará lo correcto". NO es inteligente y no te salvará. Es un poco menos seguro que copiar registros de transacciones binarias desde la línea de comandos y reproducirlos a mano. Aún así, desconectar a todos los clientes del antiguo maestro y cambiarlos al nuevo maestro se puede hacer en cuestión de segundos, mucho más rápido que esperar una actualización de esquema de varias horas.
  • Ahora el Maestro B es tu nuevo maestro. Tienes el nuevo esquema. La vida es buena. Toma una cerveza; lo peor ya pasó.
  • Repite el proceso con el Maestro A, actualizando su esquema para que se convierta en tu nuevo maestro secundario, listo para asumir el control en caso de que tu maestro primario (el maestro B ahora) pierda poder o simplemente muera sobre ti.

Una manera fácil de actualizar el esquema no lo es. Funcionable en un entorno de producción serio; sí lo es. Por favor, por favor, por favor, si hay una manera más fácil de agregar un índice a una tabla MySQL sin bloquear escrituras, hágamelo saber.

Buscar en Google me llevó a este artículo que describe una técnica similar. Aún mejor, recomiendan beber en el mismo punto del procedimiento (tenga en cuenta que escribí mi respuesta antes de leer el artículo).

Pt-online-schema-change de Percona

El artículo que vinculé anteriormente habla sobre una herramienta, pt-online-schema-change , que funciona de la siguiente manera:

  • Crea una nueva tabla con la misma estructura que la original.
  • Actualice el esquema en una nueva tabla.
  • Agregue un disparador en la tabla original para que los cambios se mantengan sincronizados con la copia
  • Copie filas en lotes de la tabla original.
  • Mueva la mesa original a un lado y reemplácela por una nueva.
  • Suelta la mesa vieja.

Yo nunca probé la herramienta. YMMV

RDS

Actualmente estoy usando MySQL a través del RDS de Amazon . Es un servicio realmente ingenioso que concluye y administra MySQL, lo que le permite agregar nuevas réplicas de lectura con un solo botón y actualizar de forma transparente la base de datos en todos los SKU de hardware. Es realmente conveniente. No tienes acceso SUPER a la base de datos, por lo que no puedes jugar con la replicación directamente (¿es esto una bendición o una maldición?). Sin embargo, puede usar Promoción de réplica de lectura para realizar cambios en el esquema en un esclavo de solo lectura y luego promover ese esclavo para que se convierta en su nuevo maestro. Exactamente el mismo truco que describí anteriormente, pero mucho más fácil de ejecutar. Todavía no hacen mucho para ayudarte con el corte. Tienes que reconfigurar y reiniciar tu aplicación.

Dave Dopson
fuente
3
pt-online-schema-change funciona muy bien incluso en una replicación maestro-esclavo. Lo he usado para realizar una migración en vivo en una tabla de más de 20 millones de registros de lectura ocupada en nuestra base de datos maestra de producción con 2 esclavos de replicación sin ningún problema ni tiempo de inactividad. Se necesita algo de tiempo para preparar la secuencia de comandos, y normalmente tengo que crear un archivo .sql que contiene el cambio de SQL sin procesar y un archivo .sh como contenedor para ejecutar el mismo SQL pero en formato de fragmento (sin ALTER TABLE). Puede ejecutar varios comandos con pt-online-schema-change colocándolos y separados por comas.
Alex Le
-1; No sé acerca de las versiones anteriores, pero que la creación de índices no bloquea DML concurrente en MySQL 5.6+ (para el cual existía un RC en el momento en que se escribió esta respuesta, y que se lanzó oficialmente cuando duró esta respuesta editado en mayo de 2013) porque he confiado en esto para ejecutar creaciones de índices de varias horas en tablas de producción sin dejar de aceptar inserciones. Y aunque puede tener razón sobre la creación de índices que bloquea DML en 5.5 y versiones inferiores, el retraso de menos de un segundo que se muestra aquí no es del todo convincente.
Mark Amery
@MarkAmery: el comportamiento de bloqueo es un comportamiento de bloqueo, y 400 ms es una eternidad. Bloques de MySQL 5.5 para actualizaciones de índices. Cree una base de datos de prueba más grande y se bloqueará durante segundos, horas o días. Escribí esta publicación antes de que MySQL 5.6 tuviera actualizaciones de esquema en línea, por lo que mi contenido original no refleja ese hecho. Actualicé la publicación para reflejar la información disponible recientemente.
Dave Dopson
@DaveDopson, ¿está 100% seguro de que solo se bloquean las operaciones de ACTUALIZACIÓN?
toto_tico
Ese fue el caso de la versión que probé.
Dave Dopson
67

Como se describe en esta publicación de blog , el ALTER TABLEmecanismo InnoDB se ha rediseñado por completo para MySQL 5.6.

(Para obtener una descripción general exclusiva de este tema, la documentación de MySQL puede proporcionar una lectura de la tarde).

Para agregar un índice a una tabla sin un bloqueo resultante en UPDATE/ INSERT, se puede usar el siguiente formato de declaración:

ALTER TABLE my_table ADD INDEX my_table__idx (my_column), ALGORITHM=INPLACE, LOCK=NONE;
Dibujó
fuente
4
Advertencia: dba.stackexchange.com/questions/138363/…
Alexander Torstling
16

Actualización de MySQL 5.6 (febrero de 2013): ahora puede realizar operaciones de lectura y escritura mientras se crea un índice incluso con tablas InnoDB: http://dev.mysql.com/doc/refman/5.6/en/innodb-create-index -overview.html

En MySQL 5.6 y superior, la tabla permanece disponible para operaciones de lectura y escritura mientras se crea o quita el índice. La instrucción CREATE INDEX o DROP INDEX solo finaliza después de que se completan todas las transacciones que acceden a la tabla, de modo que el estado inicial del índice refleja el contenido más reciente de la tabla. Anteriormente, la modificación de la tabla mientras se creaba o quitaba un índice normalmente provocaba un interbloqueo que cancelaba la instrucción INSERT, UPDATE o DELETE en la tabla.

y:

En MySQL 5.6, esta característica se vuelve más general: puede leer y escribir en tablas mientras se crea un índice, y se pueden realizar muchos más tipos de operaciones ALTER TABLE sin copiar la tabla, sin bloquear las operaciones DML o ambas cosas. Por lo tanto, en MySQL 5.6 y versiones posteriores, normalmente nos referimos a este conjunto de características como DDL en línea en lugar de Creación rápida de índices.

de http://dev.mysql.com/doc/refman/5.6/en/glossary.html#glos_fast_index_creation

Eric Saboia
fuente
Entonces, ¿cómo se puede explicar el análisis de Dave?
Nikhil Sahu
1
@NikhilSahu Dave claramente no estaba probando en MySQL 5.6, sino en alguna versión anterior. Tenga en cuenta que 5.6 aún no se publicó en el momento en que Dave publicó la revisión inicial de su respuesta.
Mark Amery
+1. Mi análisis fue en MySQL 5.5 (el último que estaba disponible en 2013). Estoy actualizando mi respuesta para reflejar las nuevas capacidades en MySQL 5.6.
Dave Dopson
3

pt-online-schema-change es el camino a seguir si realmente quiere asegurarse de que la migración no colapse el sitio.

Como escribí en el comentario anterior, tengo varias experiencias con pt-online-schema-change en producción. Tenemos nuestra tabla principal de más de 20 millones de registros y un maestro -> 2 esclavos de replicación de solo lectura. He realizado al menos una docena de migraciones con pt-online-schema-change desde agregar una nueva columna, cambiar el juego de caracteres, hasta agregar varios índices. También atendemos toneladas de tráfico durante el tiempo de migración y no hemos tenido ningún contratiempo. Por supuesto, tendría que probar todos los scripts muy a fondo antes de ejecutarlos en producción.

Intenté agrupar los cambios en 1 secuencia de comandos para que pt-online-schema-change solo tenga que copiar los datos una vez. Y tenga mucho cuidado al cambiar el nombre de la columna, ya que perderá sus datos. Sin embargo, agregar un índice debería estar bien.

Alex Le
fuente
No estoy de acuerdo con su recomendación incondicional de pt-online-schema-change. Es genial, pero es excesivo para muchas situaciones en las que las capacidades DDL en línea de MySQL 5.6 + ya funcionan bien. También tiene limitaciones (como no jugar bien con los disparadores) y duplica la cantidad de escritura necesaria por inserción en la tabla original mientras se realiza un cambio de esquema. Gravará su disco significativamente más que un cambio de esquema en línea ordinario, y por lo tanto tiene el potencial de "derribar su sitio" en circunstancias en las que simplemente ejecutar el cambio de esquema de la manera simple hubiera funcionado bien.
Mark Amery
Escribí basándome en mi experiencia real con pt-online-schema-change en ese momento, así que no estoy seguro de por qué llamarías a mi recomendación "no calificada". Teníamos al menos más de 1000 visitantes en el sitio en un momento dado cuando ejecuté los cambios de esquema y, por supuesto, la IO del disco era una carga pesada, pero nuestro sitio no se cayó. Tener un buen almacenamiento en caché también ayudó. No he utilizado MySQL 5.6+ online DDL pero, según mi experiencia, pt-online-schema-change hizo bien su trabajo en nuestro caso.
Alex Le
1
@AlexYe Yikes, quise decir "no calificado" en el sentido de "sin reservas" en lugar del sentido de "entregado por alguien que no está calificado para comentar". Esta última interpretación no se me ocurrió hasta que vi su comentario y ciertamente no lo es. ¡No es lo que pretendía! es decir, estaba diciendo que si bien pt-online-schema-changees una herramienta útil, hay muchas situaciones en las que el DDL en línea ordinario es igual de bueno y un puñado en el que es mejor, por lo que cualquier recomendación debe ser cuidadosamente advertida en lugar de universal.
Mark Amery