Tengo una tabla de Postgres con ~ 2.1 millones de filas. Ejecuté la siguiente actualización:
WITH stops AS (
SELECT id,
rank() OVER (ORDER BY offense_timestamp,
defendant_dl,
offense_street_number,
offense_street_name) AS stop
FROM consistent.master
WHERE citing_jurisdiction=1
)
UPDATE consistent.master
SET arrest_id=stops.stop
FROM stops
WHERE master.id = stops.id;
Esta consulta tardó 39 horas en ejecutarse. Estoy ejecutando esto en un procesador de computadora portátil i7 Q720 de 4 núcleos (físico), mucha RAM, nada más funcionando la gran mayoría de las veces. Sin restricciones de espacio en el disco duro. La mesa había sido aspirada, analizada y reindexada recientemente.
Durante todo el tiempo en que se ejecutó la consulta, al menos después de que se WITH
completó la inicial , el uso de la CPU generalmente era bajo y el HDD estaba en uso al 100%. El HDD se usaba tan fuerte que cualquier otra aplicación funcionaba mucho más lentamente de lo normal.
La configuración de energía de la computadora portátil estaba en Alto rendimiento (Windows 7 x64).
Aquí está el EXPLICAR:
Update on master (cost=822243.22..1021456.89 rows=2060910 width=312)
CTE stops
-> WindowAgg (cost=529826.95..581349.70 rows=2060910 width=33)
-> Sort (cost=529826.95..534979.23 rows=2060910 width=33)
Sort Key: consistent.master.offense_timestamp, consistent.master.defendant_dl, consistent.master.offense_street_number, consistent.master.offense_street_name
-> Seq Scan on master (cost=0.00..144630.06 rows=2060910 width=33)
Filter: (citing_jurisdiction = 1)
-> Hash Join (cost=240893.51..440107.19 rows=2060910 width=312)
Hash Cond: (stops.id = consistent.master.id)
-> CTE Scan on stops (cost=0.00..41218.20 rows=2060910 width=48)
-> Hash (cost=139413.45..139413.45 rows=2086645 width=268)
-> Seq Scan on master (cost=0.00..139413.45 rows=2086645 width=268)
citing_jurisdiction=1
solo excluye unas pocas decenas de miles de filas. Incluso con esa WHERE
cláusula, sigo operando en más de 2 millones de filas.
El disco duro está encriptado con TrueCrypt 7.1a. Que ralentiza las cosas un poco, pero no lo suficiente para causar una consulta a tomar que muchas horas.
La WITH
parte solo tarda unos 3 minutos en ejecutarse.
El arrest_id
campo no tenía índice para clave externa. Hay 8 índices y 2 claves foráneas en esta tabla. Todos los demás campos de la consulta están indexados.
El arrest_id
campo no tenía restricciones excepto NOT NULL
.
La tabla tiene 32 columnas en total.
arrest_id
Es de tipo carácter variable (20) . Me doy cuenta de que rank()
produce un valor numérico, pero tengo que usar caracteres que varían (20) porque tengo otras filas citing_jurisdiction<>1
que usan datos no numéricos para este campo.
El arrest_id
campo estaba en blanco para todas las filas con citing_jurisdiction=1
.
Este es un portátil personal de alta gama (desde hace 1 año). Soy el único usuario. No se estaban ejecutando otras consultas u operaciones. El bloqueo parece poco probable.
No hay desencadenantes en ningún lugar de esta tabla ni en ningún otro lugar de la base de datos.
Otras operaciones en esta base de datos nunca toman una cantidad de tiempo anormal. Con una indexación adecuada, las SELECT
consultas suelen ser bastante rápidas.
fuente
Seq Scan
son un poco atemorizantes ...Respuestas:
Hace poco sucedió algo similar con una tabla de 3.5 millones de filas. Mi actualización nunca terminaría. Después de muchos experimentos y frustración, finalmente encontré al culpable. Resultó ser los índices en la tabla que se está actualizando.
La solución fue eliminar todos los índices en la tabla que se actualizaba antes de ejecutar la declaración de actualización. Una vez que hice eso, la actualización terminó en unos minutos. Una vez que se completó la actualización, volví a crear los índices y volví al negocio. Esto probablemente no lo ayudará en este momento, pero puede ser que alguien más esté buscando respuestas.
Mantendría los índices en la tabla de la que está extrayendo los datos. Ese no tendrá que seguir actualizando ningún índice y debería ayudar a encontrar los datos que desea actualizar. Funcionó bien en una computadora portátil lenta.
fuente
Su mayor problema es hacer grandes cantidades de trabajo pesado de escritura y búsqueda en el disco duro de una computadora portátil. Eso nunca va a ser rápido, no importa lo que haga, especialmente si es el tipo de unidad de 5400 RPM más lenta que se envía en muchas computadoras portátiles.
TrueCrypt ralentiza las cosas más de "un poco" para las escrituras. Las lecturas serán razonablemente rápidas, pero las escrituras hacen que RAID 5 parezca rápido. Ejecutar una base de datos en un volumen TrueCrypt será una tortura para las escrituras, especialmente las escrituras aleatorias.
En este caso, creo que estaría perdiendo el tiempo tratando de optimizar la consulta. Estás reescribiendo la mayoría de las filas de todos modos, y va a ser lento con tu horrible situación de escritura. Lo que recomendaría es:
Sospecho que será más rápido que simplemente soltar y volver a crear las restricciones solo, porque una ACTUALIZACIÓN tendrá patrones de escritura bastante aleatorios que matarán su almacenamiento. Dos inserciones masivas, una en una tabla no registrada y otra en una tabla registrada WAL sin restricciones, probablemente serán más rápidas.
Si tiene copias de seguridad absolutamente actualizadas y no le importa tener que restaurar su base de datos a partir de copias de seguridad , también puede reiniciar PostgreSQL con el
fsync=off
parámetro yfull_page_writes=off
temporalmente para esta operación masiva. Cualquier problema inesperado como pérdida de energía o un bloqueo del sistema operativo dejará su base de datos irrecuperable mientrasfsync=off
.El POSTGreSQL equivalente a "sin registro" es usar tablas no registradas. Estas tablas no registradas se truncan si la base de datos se cierra sin limpiar mientras están sucias. El uso de tablas no registradas al menos reducirá a la mitad su carga de escritura y reducirá el número de búsquedas, por lo que pueden ser MUCHO más rápidas.
Al igual que en Oracle, puede ser una buena idea eliminar un índice y luego volver a crearlo después de una gran actualización por lotes. El planificador de PostgreSQL no puede determinar que se está llevando a cabo una gran actualización, pausar las actualizaciones del índice y luego reconstruir el índice al final; incluso si pudiera, sería muy difícil averiguar en qué punto valía la pena hacerlo, especialmente de antemano.
fuente
HOT
entonces es mejor dejar los índices en su lugar. De lo contrario, es probable que desee soltar y volver a crear. La columna no está indexada, pero para poder realizar una actualización CALIENTE también debe haber espacio libre en la misma página, por lo que depende un poco de cuánto espacio muerto haya en la tabla. Si se trata principalmente de escritura, diría que descarte todos los índices. Si se actualiza mucho, puede tener agujeros y puede estar bien. Herramientas comopageinspect
ypg_freespacemap
pueden ayudar a determinar esto.Alguien dará una mejor respuesta para Postgres, pero aquí hay algunas observaciones desde una perspectiva de Oracle que pueden aplicarse (y los comentarios son demasiado largos para el campo de comentarios).
Mi primera preocupación sería tratar de actualizar 2 millones de filas en una transacción. En Oracle, estaría escribiendo una imagen anterior de cada bloque que se está actualizando para que otra sesión aún tenga una lectura consistente sin leer sus bloques modificados y tenga la capacidad de revertir. Esa es una larga reversión que se está construyendo. Por lo general, es mejor hacer las transacciones en pequeños trozos. Diga 1,000 registros a la vez.
Si tiene índices en la tabla, y la tabla se considerará fuera de servicio durante el mantenimiento, a menudo es mejor eliminar los índices antes de una operación grande y luego volver a crearla después. Más barato entonces constantemente tratando de mantener los índices con cada registro actualizado.
Oracle permite sugerencias de "no registro" en las declaraciones para detener el diario. Acelera mucho las declaraciones, pero deja su base de datos en una situación "irrecuperable". Por lo tanto, desearía hacer una copia de seguridad antes y otra vez inmediatamente después. No sé si Postgres tiene opciones similares.
fuente
VACUUM
es solo la mitad de la respuesta; PostgreSQL también utiliza el llamado "registro de escritura anticipada" (efectivamente un diario) para proporcionar confirmaciones atómicas y proteger contra escrituras parciales, etc. Ver postgresql.org/docs/current/static/wal-intro.html