sql >> Base de Datos >  >> RDS >> Mysql

La inserción de MySql en la consulta de selección es demasiado lenta para copiar 100 millones de filas

Cualquier INSERT ... SELECT ... la consulta adquiere un bloqueo COMPARTIDO en las filas que lee de la tabla de origen en SELECT. Pero al procesar fragmentos de filas más pequeños, el bloqueo no dura demasiado.

La consulta con LIMIT ... OFFSET va a ser cada vez más lento a medida que avanza en la tabla de origen. Con 10 000 filas por fragmento, debe ejecutar esa consulta 10 000 veces, cada una debe comenzar de nuevo y escanear la tabla para llegar al nuevo DESPLAZAMIENTO.

No importa lo que haga, copiar 100 millones de filas llevará un tiempo. Está haciendo mucho trabajo.

Usaría pt-archiver , una herramienta gratuita diseñada para este propósito. Procesa las filas en "trozos" (o subconjuntos). Ajustará dinámicamente el tamaño de los fragmentos para que cada fragmento tarde 0,5 segundos.

La mayor diferencia entre su método y pt-archiver es que pt-archiver no usa LIMIT ... OFFSET , recorre el índice de clave principal, seleccionando fragmentos de fila por valor en lugar de por posición. Por lo tanto, cada fragmento se lee de manera más eficiente.

Re tu comentario:

Espero que reducir el tamaño del lote y aumentar el número de iteraciones haga que el problema de rendimiento empeore , no mejor.

La razón es que cuando usas LIMIT con OFFSET , cada consulta debe comenzar de nuevo al comienzo de la tabla y contar las filas hasta el OFFSET valor. Esto se vuelve más y más largo a medida que recorres la tabla.

Ejecutar 20 000 consultas costosas usando OFFSET tomará más tiempo que ejecutar 10,000 consultas similares. La parte más costosa no será leer 5000 o 10 000 filas, ni insertarlas en la tabla de destino. La parte costosa se saltará ~50 000 000 de filas, una y otra vez.

En su lugar, debe iterar sobre la tabla por valores no por compensaciones.

INSERT IGNORE INTO Table2(id, field2, field3)
        SELECT f1, f2, f3
        FROM Table1
        WHERE id BETWEEN rowOffset AND rowOffset+limitSize;

Antes del bucle, consulte MIN(id) y MAX(id) e inicie rowOffset en el valor mínimo y en bucle hasta el valor máximo.

Así es como funciona pt-archiver.