¿Cómo copiar eficientemente millones de filas de una tabla a otra en Postgresql?


37

Tengo dos tablas de base de datos. Uno contiene cientos de millones de registros. Llamemos a eso history. El otro se calcula a diario y quiero copiar todos sus registros en el history.

Lo que hice fue correr:

INSERT INTO history SELECT * FROM daily

E hizo el truco por un tiempo, pero comenzó a volverse cada vez más lento a medida que el número de registros seguía creciendo. Ahora tengo alrededor de 2 millones de discos que necesitan ser copiados de dailyque historyen una sola operación y se tarda mucho tiempo en completarse.

¿Existe otra forma más eficiente de copiar datos de una tabla a otra?

Respuestas:


10

Si planea mantener el historial durante largos períodos (muchos meses), le sugiero que eche un vistazo a las opciones de partición: puede ser una partición para cada día o semana, etc. Depende también de los patrones de acceso de su tabla de historial (¿ejecuta consultas que acceden a datos entre fechas? ¿Hace muchas agregaciones, etc.). Eche un vistazo a las vistas materializadas para almacenar agregados / resúmenes. http://www.postgresql.org/docs/9.3/static/ddl-partitions.html http://www.postgresql.org/docs/9.3/static/sql-creatematerializedview.html


Gracias por la respuesta. Parece que el único camino a seguir. Necesitaría particionar los datos por meses y hacer que la reindexación (ya que la regeneración del índice era un problema aquí) fuera mucho más rápida.
Milovan Zogovic

16

Volcar la tabla en formato csv

COPY table TO '/tmp/table.csv' DELIMITER ',';

use el comando COPIAR, que es mucho más eficiente para grandes cantidades de datos.

COPY table FROM '/tmp/table.csv' DELIMITER ',';

Consulte los documentos de Postgres en http://www.postgresql.org/docs/current/static/sql-copy.html para obtener más información


1
Sigue funcionando muy, muy lento ... ¿Quizás tenga que ver con tener que reconstruir un índice tan grande? Hay 160 millones de filas en la historytabla, y estamos agregando 3 millones más de filas.
Milovan Zogovic

2
Si está completando una tabla vacía o agregando más filas de las que ya existen, generalmente es más eficiente descartar índices no agrupados y volver a crearlos una vez que se completa la transferencia (a menos que haya un uso activo de la (s) tabla (s) en ese momento )
David Spillett

Por cierto, ¿es esta una operación única o es algo que tienes que hacer regularmente? Si es de forma regular, sugiero que crees un disparador para que no tengas que pasar por esta prueba cada vez.
Fabrizio Mazzoni

@FabrizioMazzoni: debe realizarse a diario en un momento específico (un poco tomando instantáneas a tiempo).
Milovan Zogovic

@DavidSpillett - de hecho! La eliminación de índices hace que la importación sea muy rápida (vea mi respuesta más arriba), sin embargo, la recreación de índices lleva horas (ya que tengo 160 millones de filas en la base de datos) ...
Milovan Zogovic

14

El problema fue con los índices. La historytabla tenía 160 millones de filas indexadas. Al ejecutar cualquiera de los dos, COPY FROMo INSERT INTO .. SELECTllevaba mucho tiempo no insertar filas, sino actualizar índices. Cuando deshabilité los índices, importó 3 millones de filas en 10 segundos. Ahora necesito encontrar una forma más rápida de reindexar la tabla grande.


3
¿Incluso necesita índices en una tabla de historial?
Sherlock

2
Agregue el índice usando la palabra clave CONCURRENTEMENTE
Akvel

11

Puede usar la herramienta psql , podría ser eficiente, como lo siguiente,

psql -h ${DAILY_HOST_IP} -p ${PG_PORT} ${DB_NAME} ${USER_NAME} -c "copy daily to stdout " | psql -h ${HISTORY_HOST_IP} -p ${PG_PORT} ${DB_NAME} ${USER_NAME}  -c "copy history from stdin"

También puedes escribir un script de shell.


Gran solución sin archivo intermedio. Muy rápido también, copié una tabla de 950 millones de filas en 1h20 (sin índices) entre el disco normal y el sistema de archivos de red.
Le Droid el

Es una verdadera pena que esto no se pueda hacer directamente de una mesa a otra.
Charlie Clark

3

Por supuesto, esta no es una respuesta exacta a su pregunta, pero si no necesita acceder a la historytabla, también puede generar un volcado de SQL:

pg_dump -h host -p port -w -U user db > dump.sql

Entonces uno podría usar una herramienta como gitcalcular la diferencia y almacenarla de manera eficiente.

git add dump.sql
git commit -m "temp dump"
git gc --aggressive

Esto es útil porque la mayoría de las partes de una base de datos no cambiarán todos los días. En lugar de almacenar una copia completa para cada día, uno puede almacenar la diferencia entre dos días.

Puede usar un crontabtrabajo para que el volcado se procese todos los días.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.