Cómo acelerar el rendimiento de inserción en PostgreSQL
Estoy probando el rendimiento de inserción de Postgres. Tengo una tabla con una columna con un número como tipo de datos. También hay un índice. Llené la base de datos usando esta consulta:
insert into aNumber (id) values (564),(43536),(34560) ...
Inserté 4 millones de filas muy rápidamente, 10.000 a la vez con la consulta anterior. Después de que la base de datos alcanzó los 6 millones de filas, el rendimiento disminuyó drásticamente a 1 millón de filas cada 15 minutos. ¿Existe algún truco para aumentar el rendimiento de la inserción? Necesito un rendimiento de inserción óptimo en este proyecto.
Usando Windows 7 Pro en una máquina con 5 GB de RAM.
Consulte poblar una base de datos en el manual de PostgreSQL, el excelente artículo de Depesz sobre el tema, como siempre, y esta pregunta SO .
(Tenga en cuenta que esta respuesta trata sobre la carga masiva de datos en una base de datos existente o para crear una nueva. Si está interesado en restaurar el rendimiento de la base de datos pg_restore
o en psql
la ejecución de pg_dump
la salida, gran parte de esto no se aplica desde entonces pg_dump
y pg_restore
ya hace cosas como crear desencadenadores e índices después de finalizar una restauración de esquema+datos) .
Hay mucho por hacer. La solución ideal sería importar a una UNLOGGED
tabla sin índices, luego cambiarla a registrada y agregar los índices. Desafortunadamente, en PostgreSQL 9.4 no hay soporte para cambiar tablas UNLOGGED
a registradas. 9.5 agrega ALTER TABLE ... SET LOGGED
para permitirle hacer esto.
Si puede desconectar su base de datos para la importación masiva, utilice pg_bulkload
.
De lo contrario:
Deshabilite cualquier desencadenante en la mesa
Elimine los índices antes de iniciar la importación y vuelva a crearlos después. (Se necesita mucho menos tiempo para crear un índice de una sola vez que para agregarle los mismos datos progresivamente, y el índice resultante es mucho más compacto).
Si realiza la importación dentro de una sola transacción, es seguro eliminar las restricciones de clave externa, realizar la importación y volver a crear las restricciones antes de confirmar. No hagas esto si la importación se divide en varias transacciones, ya que podrías introducir datos no válidos.
Si es posible, utilice
COPY
en lugar deINSERT
sSi no puede usarlo,
COPY
considere usar s de valores múltiplesINSERT
si es práctico. Parece que ya estás haciendo esto. Sin embargo , no intente enumerar demasiados valores en uno soloVALUES
; esos valores tienen que caber en la memoria un par de veces, así que manténgalos en unos pocos cientos por declaración.Combine sus inserciones en transacciones explícitas, realizando cientos de miles o millones de inserciones por transacción. No existe un límite práctico, AFAIK, pero el procesamiento por lotes le permitirá recuperarse de un error marcando el inicio de cada lote en sus datos de entrada. Nuevamente, parece que ya estás haciendo esto.
Utilice
synchronous_commit=off
y enormecommit_delay
para reducir los costos de fsync(). Sin embargo, esto no ayudará mucho si ha agrupado su trabajo en grandes transacciones.INSERT
oCOPY
en paralelo desde varias conexiones. La cantidad depende del subsistema de disco de su hardware; Como regla general, desea una conexión por disco duro físico si utiliza almacenamiento conectado directamente.Establezca un
max_wal_size
valor alto (checkpoint_segments
en versiones anteriores) y habilítelolog_checkpoints
. Mire los registros de PostgreSQL y asegúrese de que no se quejen de que los puntos de control ocurren con demasiada frecuencia.Si y solo si no le importa perder todo su clúster PostgreSQL (su base de datos y cualquier otra en el mismo clúster) debido a una corrupción catastrófica si el sistema falla durante la importación, puede detener Pg, configurar
fsync=off
, iniciar Pg, realizar su importación, luego (vitalmente) detenga Pg yfsync=on
vuelva a configurar. Ver configuración WAL . No haga esto si ya hay datos que le interesan en alguna base de datos de su instalación de PostgreSQL. Si configura,fsync=off
también puede configurarfull_page_writes=off
; Nuevamente, recuerde volver a activarlo después de la importación para evitar la corrupción y la pérdida de datos en la base de datos. Ver configuraciones no duraderas en el manual de la página.
También debería considerar ajustar su sistema:
Utilice SSD de buena calidad para el almacenamiento tanto como sea posible. Los buenos SSD con cachés de reescritura confiables y con protección energética hacen que las tasas de confirmación sean increíblemente rápidas. Son menos beneficiosos cuando sigues los consejos anteriores, que reducen los vaciados de disco/número de
fsync()
s, pero aún así pueden ser de gran ayuda. No utilice SSD baratos sin la protección adecuada contra fallas de energía a menos que no le importe conservar sus datos.Si está utilizando RAID 5 o RAID 6 para almacenamiento adjunto directo, deténgase ahora. Haga una copia de seguridad de sus datos, reestructure su matriz RAID a RAID 10 y vuelva a intentarlo. Los RAID 5/6 no tienen remedio para el rendimiento de escritura masiva, aunque un buen controlador RAID con una gran caché puede ayudar.
Si tiene la opción de utilizar un controlador RAID de hardware con una gran caché de reescritura respaldada por batería, esto realmente puede mejorar el rendimiento de escritura para cargas de trabajo con muchas confirmaciones. No ayuda mucho si usas una confirmación asíncrona con commit_delay o si realizas menos transacciones grandes durante la carga masiva.
Si es posible, almacene WAL (
pg_wal
opg_xlog
en versiones anteriores) en un disco/matriz de discos independiente. No tiene mucho sentido utilizar un sistema de archivos independiente en el mismo disco. La gente suele optar por utilizar un par RAID1 para WAL. Nuevamente, esto tiene más efecto en sistemas con altas tasas de confirmación y tiene poco efecto si utiliza una tabla no registrada como destino de carga de datos.
También te puede interesar Optimizar PostgreSQL para realizar pruebas rápidas .
Hoy pasé alrededor de 6 horas en el mismo tema. Las inserciones van a una velocidad "normal" (menos de 3 segundos por 100K) hasta filas de 5MI (de un total de 30MI) y luego el rendimiento cae drásticamente (hasta 1min por 100K).
No enumeraré todas las cosas que no funcionaron y iré directo al grano.
Dejé caer una clave principal en la tabla de destino (que era un GUID) y mis 30MI o filas fluyeron felizmente hacia su destino a una velocidad constante de menos de 3 segundos por 100K.