Estoy probando el rendimiento de inserción de Postgres. Tengo una tabla con una columna con número como tipo de datos. También hay un índice en él. 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 alcanzara los 6 millones de filas, el rendimiento disminuyó drásticamente a 1 millón de filas cada 15 minutos. ¿Hay algún truco para aumentar el rendimiento de 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.
sql
postgresql
bulkinsert
sql-insert
Lucas101
fuente
fuente
Respuestas:
Consulte llenar una base de datos en el manual de PostgreSQL, el excelente artículo de Depesz sobre el tema y esta pregunta SO .
(Tenga en cuenta que esta respuesta es sobre la carga masiva de datos en una base de datos existente o para crear una nueva. Si está interesado en el rendimiento de restauración de base de datos
pg_restore
o lapsql
ejecución de lapg_dump
salida, gran parte de esto no se aplica desde entoncespg_dump
ypg_restore
ya hace cosas como crear dispara e indexa después de que termina un esquema + restauración de datos) .Hay mucho por hacer. La solución ideal sería importar en una
UNLOGGED
tabla sin índices, luego cambiarlo a registrado y agregar los índices. Desafortunadamente, en PostgreSQL 9.4 no hay soporte para cambiar tablas deUNLOGGED
a registradas. 9.5 agregaALTER TABLE ... SET LOGGED
para permitirle hacer esto.Si puede desconectar su base de datos para la importación masiva, use
pg_bulkload
.De otra manera:
Deshabilitar cualquier desencadenante en la mesa
Descarte los índices antes de comenzar la importación, vuelva a crearlos después. (Se necesita mucho menos tiempo para crear un índice en una pasada que agregar 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 haga esto si la importación se divide en varias transacciones, ya que podría introducir datos no válidos.
Si es posible, use en
COPY
lugar deINSERT
sSi no puede usar,
COPY
considere usarINSERT
s de valores múltiples si es práctico. Parece que ya estás haciendo esto. Sin embargo, no intente enumerar demasiados valores en un soloVALUES
; esos valores tienen que caber en la memoria un par de veces, así que manténgalos en unos pocos cientos por declaración.Lote sus inserciones en transacciones explícitas, haciendo 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 al marcar el inicio de cada lote en sus datos de entrada. De nuevo, parece que ya estás haciendo esto.
Uso
synchronous_commit=off
y un 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 usa almacenamiento conectado directamente.Establezca un
checkpoint_segments
valor alto y habilítelolog_checkpoints
. Mire los registros de PostgreSQL y asegúrese de que no se queje de que los puntos de control ocurran con demasiada frecuencia.Si y solo si no le importa perder todo su clúster de PostgreSQL (su base de datos y cualquier otra en el mismo clúster) por una corrupción catastrófica si el sistema se bloquea durante la importación, puede detener Pg, configurar
fsync=off
, iniciar Pg, hacer su importación, luego (vitalmente) detiene Pg y establece defsync=on
nuevo. Ver configuración de WAL . No haga esto si ya hay datos que le interesan en alguna base de datos en su instalación de PostgreSQL. Si configurafsync=off
también puede configurarfull_page_writes=off
; nuevamente, solo recuerde volver a encenderlo después de la importación para evitar la corrupción de la base de datos y la pérdida de datos. Ver configuraciones no duraderas en el manual de Pg.También debe considerar ajustar su sistema:
Utilice SSD de buena calidad para almacenar tanto como sea posible. Los buenos SSD con cachés de escritura confiables y protegidos contra la energía hacen que las tasas de confirmación sean increíblemente más rápidas. Son menos beneficiosos cuando sigue los consejos anteriores, que reducen los enjuagues de disco / número de
fsync()
s, pero aún pueden ser de gran ayuda. No use unidades de estado sólido baratas 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 el almacenamiento conectado directo, deténgase ahora. Haga una copia de seguridad de sus datos, reestructura su matriz RAID a RAID 10 e intente nuevamente. RAID 5/6 son inútiles para el rendimiento de escritura masiva, aunque un buen controlador RAID con una gran caché puede ayudar.
Si tiene la opción de usar un controlador RAID de hardware con una gran memoria caché de escritura respaldada por batería, esto realmente puede mejorar el rendimiento de escritura para cargas de trabajo con muchas confirmaciones. No ayuda tanto si está utilizando la confirmación asíncrona con commit_delay o si está haciendo menos grandes transacciones durante la carga masiva.
Si es posible, almacene WAL (
pg_xlog
) en un disco / matriz de discos por separado. No tiene mucho sentido usar un sistema de archivos separado en el mismo disco. Las personas a menudo eligen usar un par RAID1 para WAL. Nuevamente, esto tiene más efecto en los sistemas con altas tasas de compromiso, y tiene poco efecto si está utilizando una tabla no registrada como objetivo de carga de datos.También puede estar interesado en Optimizar PostgreSQL para realizar pruebas rápidas .
fuente
indisvalid
( postgresql.org/docs/8.3/static/catalog-pg-index.html ) en falso, luego cargamos datos y luego ponemos en línea los índicesREINDEX
?UNLOGGED
? Una prueba rápida muestra algo así como una mejora del 10-20%.El uso
COPY table TO ... WITH BINARY
que está de acuerdo con la documentación es " algo más rápido que los formatos de texto y CSV ". Solo haga esto si tiene millones de filas para insertar y si se siente cómodo con los datos binarios.Aquí hay una receta de ejemplo en Python, usando psycopg2 con entrada binaria .
fuente
Además de la excelente publicación de Craig Ringer y la publicación de blog de Depesz, si desea acelerar sus inserciones a través de la interfaz ODBC ( psqlodbc ) mediante el uso de inserciones de declaraciones preparadas dentro de una transacción, hay algunas cosas adicionales que debe hacer para hacerlo trabaja rapido:
Protocol=-1
en la cadena de conexión. De forma predeterminada, psqlodbc usa el nivel "Sentencia", que crea un SAVEPOINT para cada instrucción en lugar de una transacción completa, lo que hace que las inserciones sean más lentas.UseServerSidePrepare=1
en la cadena de conexión. Sin esta opción, el cliente envía la declaración de inserción completa junto con cada fila que se inserta.SQLSetConnectAttr(conn, SQL_ATTR_AUTOCOMMIT, reinterpret_cast<SQLPOINTER>(SQL_AUTOCOMMIT_OFF), 0);
SQLEndTran(SQL_HANDLE_DBC, conn, SQL_COMMIT);
. No hay necesidad de abrir explícitamente una transacción.Desafortunadamente, psqlodbc "implementa"
SQLBulkOperations
al emitir una serie de instrucciones de inserción no preparadas, de modo que para lograr la inserción más rápida, uno necesita codificar los pasos anteriores manualmente.fuente
A8=30000000
en la cadena de conexión, también debe usarse para acelerar las inserciones.Pasé alrededor de 6 horas en el mismo tema hoy. 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 se reduce drásticamente (hasta 1 minuto por 100K).
No enumeraré todas las cosas que no funcionaron y cortaré directamente a la carne.
Me cayó una clave primaria en la tabla de destino (que era un GUID) y mi 30MI o filas felizmente fluía a su destino a una velocidad constante de menos de 3 segundos por 100K.
fuente
Si sucede que debe insertar columnas con UUID (que no es exactamente su caso) y agregar a la respuesta @Dennis (aún no puedo comentar), tenga en cuenta que usar gen_random_uuid () (requiere PG 9.4 y el módulo pgcrypto) es (a lot) más rápido que uuid_generate_v4 ()
vs
Además, es la forma oficial sugerida de hacerlo
Esto redujo el tiempo de inserción de ~ 2 horas a ~ 10 minutos para 3.7M de filas.
fuente
Para un rendimiento óptimo de inserción, deshabilite el índice si esa es una opción para usted. Aparte de eso, un mejor hardware (disco, memoria) también es útil.
fuente
También encontré este problema de rendimiento de inserción. Mi solución es generar algunas rutinas para terminar el trabajo de inserción. Mientras tanto,
SetMaxOpenConns
se le debe dar un número adecuado; de lo contrario, se alertarían demasiados errores de conexión abierta.La velocidad de carga es mucho más rápida para mi proyecto. Este fragmento de código acaba de dar una idea de cómo funciona. Los lectores deberían poder modificarlo fácilmente.
fuente