¿Cómo convertir una tabla de filas 66,862,521 de MyISAM a InnoDB sin desconectarse durante varias horas?

18

¿Es posible (y cómo) convertir una gran tabla MyISAM en InnoDB sin desconectar la aplicación? Requiere insertar un par de filas en esa tabla cada segundo, pero es posible suspenderla durante aproximadamente 2 minutos.

Obviamente ALTER TABLE ... engine = innodb no funcionará. Por lo tanto, tenía el plan de crear una nueva tabla con el motor innodb y copiar el contenido en ella. Y al final, suspenda el hilo de registro de la aplicación y RENAME TABLE.

Desafortunadamente, incluso copiar en lotes pequeños de 100 filas genera un retraso significativo después de un tiempo.

Editar : las filas existentes nunca cambian, esta tabla se utiliza para iniciar sesión.

Hendrik Brummermann
fuente
3
Bueno, esa pregunta es sobre minimizar el tiempo de conversación. No me importa si las conversaciones duran un par de días o semanas. Pero debe funcionar en segundo plano sin requerir tiempo de inactividad de la aplicación y sin crear un retraso notable.
Hendrik Brummermann el

Respuestas:

15

Cree una configuración Master-Master de la siguiente manera:

  • Crear segundo maestro, MasterB
  • MasterB actúa como esclavo de logTable
  • Crear logTable_newcomo innodb
  • Ejecute INSERT INTO logTable_new SELECT * FROM logTable(psuedocode) en MasterB, que envía la replicación a MasterA
  • Cuando logTable_newen MasterA termine de sincronizarse, cambie las tablas
Derek Downey
fuente
10

Dada la restricción de:

No me importa si las conversaciones duran un par de días o semanas. Pero debe funcionar en segundo plano sin requerir tiempo de inactividad de la aplicación y sin crear un retraso notable

Mientras realiza el registro, si tiene una buena manera de establecer un marcador para que pueda saber en qué momento comienza el proceso, de modo que pueda volver a aplicar los registros o hacer que los registros se escriban en un archivo de texto para luego puedes ingerirlos con LOAD DATA INFILE

Parte del problema es que escribir en lotes más pequeños significa que los índices tienen que ser recalculados una y otra vez; es mejor que lo ejecute todo de una vez, pero esto podría causar un retraso 'notable' en el sistema ... pero no tiene que hacerlo en su servidor de producción.

  1. Pause el registro o configure algún marcador para que pueda volver a aplicar los registros desde este punto en adelante.
  2. Copie su tabla MyISM a otro sistema
  3. En el otro sistema, cree una tabla InnoDB con un nombre diferente y migre los datos (incluso podría ser más rápido volcarlos y usarlos LOAD DATA INFILE)
  4. Copie la tabla InnoDB nuevamente al sistema original
  5. Establecer otro marcador para el registro.
  6. Vuelva a aplicar todos los registros a la nueva tabla entre los dos últimos marcadores.
  7. (repita los pasos 5 y 6 si el paso 6 tardó más de un minuto más o menos, hasta el momento en que solo sean unos segundos)
  8. Cambie las tablas (cambie el nombre del antiguo a table_BACKUP, uno nuevo con el nombre del anterior)
  9. Póngase al día con los registros desde el último marcador.
Joe
fuente
9

Desafortunadamente, incluso copiar en lotes pequeños de 100 filas genera un retraso significativo después de un tiempo.

¿Está agregando algún retraso entre cada lote, o simplemente agrupando las actualizaciones y ejecutando cada lote directamente después del anterior?

Si es así, intente escribir la conversión en su idioma favorito con algo como:

repeat
    copy oldest 100 rows that haven't been copied yet to new table
    sleep for as long as that update took
until there are <100 rows unprocessed
stop logging service
move the last few rows
rename tables
restart logging
delete the old table when you are sure the conversion has worked

Esto debería garantizar que la conversión no tome más de más o menos la mitad de la capacidad de su servidor, incluso permitiendo diferencias en la carga impuesta ya que el uso del sistema varía con el tiempo.

O bien, si desea utilizar la mayor cantidad de tiempo posible cuando el servicio está relativamente inactivo pero retrocediendo (posiblemente haciendo una pausa durante un período de tiempo bastante prolongado) cuando la base de datos necesita hacer algún trabajo para sus usuarios, reemplácela sleep for as long as the update tookcon if the server's load is above <upper measure>, sleep for some seconds then check again, loop around the sleep/check until the load drops below <lower measure>. Esto significará que puede avanzar en tiempos de silencio, pero se detendrá por completo cuando el servidor esté ocupado realizando su carga de trabajo normal. La determinación de la carga dependerá de su sistema operativo: en Linux y similar, el valor promedio de carga de 1 minuto de /proc/loadavgo la salida de uptimedebería hacer. <lower measure>y <upper measure>puede ser el mismo valor, aunque es habitual en controles como este tener una diferencia para que su proceso no continúe iniciando y luego pausando inmediatamente debido a que su propio reinicio influye en la medida de carga.

Por supuesto, esto no funcionaría para las tablas donde las filas antiguas pueden modificarse, pero funcionará bien para una tabla de registro como la que usted describe.

Deberá ignorar la sabiduría habitual de crear índices después de llenar la nueva tabla en este caso. Si bien eso es más eficiente cuando desea que las cosas sean lo más rápido posible (el efecto sobre el resto del sistema sea condenado), en este caso no desea el gran exceso de carga al final del proceso como el los índices se crean completamente de una vez, ya que este es un proceso que no puede pausar cuando las cosas se llenan.

David Spillett
fuente
4

Algo como esto funcionaría?

  1. Pausa el registro (para que la $auto_incrementtabla de registro mytable no cambie).
  2. Tenga en cuenta el $auto_incrementvalor usando SHOW TABLE STATUS LIKE 'mytable'.
  3. CREATE TABLE mytable_new LIKE mytable
  4. ALTER TABLE mytable_new AUTO_INCREMENT=$auto_increment ENGINE=Innodb
  5. RENAME TABLE mytable TO mytable_old, mytable_new TO mytable
  6. Habilite el registro nuevamente. La tabla Innodb ahora comenzará a poblarse.
  7. INSERT INTO mytable SELECT * FROM mytable_old.

Puede hacer el paso 7 en lotes o en una declaración, ya que no debería estar bloqueando el registro normal.

Riedsio
fuente
aún estaría bloqueando, debido a la forma en que innodb maneja el auto_incremento. de forma predeterminada, innodb toma un bloqueo de nivel de tabla cuando se inserta en una columna de aumento automático y libera el bloqueo tan pronto como finaliza la inserción.
ovais.tariq