Usando PG 9.1 en Ubuntu 12.04.
Actualmente, lleva hasta 24 horas ejecutar un gran conjunto de sentencias ACTUALIZAR en una base de datos, que son de la siguiente forma:
UPDATE table
SET field1 = constant1, field2 = constant2, ...
WHERE id = constid
(Solo estamos sobrescribiendo los campos de los objetos identificados por ID). Los valores provienen de una fuente de datos externa (no está ya en el DB en una tabla).
Las tablas tienen un puñado de índices y no hay restricciones de clave externa. No se hace ningún COMPROMISO hasta el final.
Se necesitan 2 horas para importar una pg_dump
de toda la base de datos. Esto parece una línea de base a la que deberíamos apuntar razonablemente.
A falta de producir un programa personalizado que de alguna manera reconstruya un conjunto de datos para que PostgreSQL vuelva a importar, ¿hay algo que podamos hacer para acercar el rendimiento de ACTUALIZACIÓN masiva al de la importación? (Esta es un área que creemos que los árboles de fusión con estructura de registro se manejan bien, pero nos preguntamos si hay algo que podamos hacer dentro de PostgreSQL).
Algunas ideas:
- eliminando todos los índices que no son de ID y reconstruyendo después?
- aumentando checkpoint_segments, pero ¿esto realmente ayuda a un rendimiento sostenido a largo plazo?
- utilizando las técnicas mencionadas aquí ? (Cargue los datos nuevos como tabla, luego "fusione" los datos antiguos donde la ID no se encuentra en los datos nuevos)
Básicamente hay un montón de cosas para probar y no estamos seguros de cuáles son las más efectivas o si estamos pasando por alto otras cosas. Pasaremos los próximos días experimentando, pero pensamos en preguntar aquí también.
Tengo una carga concurrente en la tabla pero es de solo lectura.
explain analyze
que está usando un índice para la búsqueda?Respuestas:
Supuestos
Como falta información en la Q, supondré:
COPY
salida, con un únicoid
por fila para coincidir con la tabla de destino.Si no es así, primero formatee correctamente o use las
COPY
opciones para manejar el formato.Eso significa que no hay acceso concurrente. De lo contrario, considere esta respuesta relacionada:
Solución
Le sugiero que siga un enfoque similar al descrito en el enlace de su tercera viñeta . Con grandes optimizaciones.
Para crear la tabla temporal, hay una forma más simple y rápida:
Un solo grande
UPDATE
de una tabla temporal dentro de la base de datos será más rápido que las actualizaciones individuales desde fuera de la base de datos en varios órdenes de magnitud.En el modelo MVCC de PostgreSQL , un
UPDATE
medio para crear una nueva versión de fila y marcar la antigua como eliminada. Eso es casi tan caro como unINSERT
y unDELETE
combinado. Además, te deja con muchas tuplas muertas. Como está actualizando toda la tabla de todos modos, sería más rápido crear una nueva tabla y soltar la anterior.Si tiene suficiente RAM disponible, configure
temp_buffers
(¡solo para esta sesión!) Lo suficientemente alto como para mantener la tabla temporal en RAM, antes de hacer cualquier otra cosa.Para obtener una estimación de la cantidad de RAM necesaria, ejecute una prueba con una muestra pequeña y use funciones de tamaño de objeto db :
Guión completo
Carga concurrente
Las operaciones concurrentes en la tabla (que descarté en los supuestos al inicio) esperarán, una vez que la tabla esté bloqueada cerca del final y fallarán tan pronto como se confirme la transacción, porque el nombre de la tabla se resuelve en su OID inmediatamente, pero la nueva tabla tiene un OID diferente. La tabla se mantiene constante, pero las operaciones concurrentes pueden obtener una excepción y deben repetirse. Detalles en esta respuesta relacionada:
ACTUALIZAR ruta
Si (tiene que) ir por la
UPDATE
ruta, suelte cualquier índice que no sea necesario durante la actualización y vuelva a crearlo después. Es mucho más barato crear un índice en una sola pieza que actualizarlo para cada fila individual. Esto también puede permitir actualizaciones CALIENTES .Esbocé un procedimiento similar usando
UPDATE
en esta respuesta estrechamente relacionada en SO .fuente
DROP TABLE
saca unAccess Exclusive Lock
. De cualquier manera, ya mencioné el requisito previo en la parte superior de mi respuesta:You can afford to drop and recreate the target table.
podría ayudar bloquear la mesa al comienzo de la transacción. Le sugiero que comience una nueva pregunta con todos los detalles relevantes de su situación para que podamos llegar al fondo de esto.CREATE TABLE tbl_new AS SELECT t.*, u.field1, u.field2 from tbl t NATURAL LEFT JOIN tmp_tbl u;
,LEFT JOIN
lo que permite mantener filas para las que no hay actualización. Por supuesto,NATURAL
se puede cambiar a cualquier válidoUSING()
oON
.Si los datos pueden estar disponibles en un archivo estructurado, puede leerlos con un contenedor de datos ajeno y realizar una fusión en la tabla de destino.
fuente
MERGE
no se implementa en PostgreSQL . Las implementaciones en otros RDBMS varían bastante. Considere la información de etiqueta paraMERGE
yUPSERT
.