Tengo una base de datos en PostgreSQL 9.2 que tiene un esquema principal con alrededor de 70 tablas y un número variable de esquemas idénticamente estructurados por cliente de 30 tablas cada uno. Los esquemas del cliente tienen claves externas que hacen referencia al esquema principal y no al revés.
Acabo de comenzar a llenar la base de datos con algunos datos reales tomados de la versión anterior. La base de datos había alcanzado aproximadamente 1.5 GB (se espera que crezca a varios 10 GB en semanas) cuando tuve que hacer una eliminación masiva en una tabla muy central en el esquema principal. Todas las claves foráneas en cuestión están marcadas EN ELIMINAR CASCADA.
No fue una sorpresa que esto llevara mucho tiempo, pero después de 12 horas quedó claro que era mejor comenzar de nuevo, dejar caer la base de datos y volver a iniciar la migración. Pero, ¿qué sucede si necesito repetir esta operación más tarde cuando el DB está activo y es mucho más grande? ¿Existen métodos alternativos y más rápidos?
¿Sería mucho más rápido si escribiera un script que explorara las tablas dependientes, comenzando en la tabla más alejada de la tabla central, eliminando las filas dependientes tabla por tabla?
Un detalle importante es que hay desencadenantes en algunas de las tablas.
Respuestas:
Tuve un problema similar. Como resultado, esos
ON DELETE CASCADE
desencadenantes estaban ralentizando un poco las cosas, porque esas eliminaciones en cascada eran muy lentas.Resolví el problema creando índices en los campos de clave externa en las tablas de referencia, y pasé de tomar un montón de horas para la eliminación a unos segundos.
fuente
ON DELETE CASCADE
)EXPLAIN (ANALYZE, BUFFERS)
consulta en una sola fila y debe mostrar qué restricciones de clave externa tomaron más tiempo (al menos lo hicieron para mí).PRIMARY
índice es suficiente, pero elUNIQUE
índice definitivamente no es lo suficientemente bueno para este propósito.Tienes pocas opciones. La mejor opción es ejecutar una eliminación por lotes para que no se disparen los disparadores. Deshabilite los desencadenantes antes de eliminarlos, luego vuelva a habilitarlos. Esto le ahorra una gran cantidad de tiempo. Por ejemplo:
Una clave importante aquí es que desea minimizar la profundidad de las subconsultas. En este caso, es posible que desee configurar tablas temporales para almacenar información relevante para evitar subconsultas profundas en su eliminación.
fuente
La mejor manera de resolver el problema es el tiempo para consultar detallada del PostgreSQL:
EXPLAIN
. Para esto, necesita encontrar como mínimo una única consulta que se complete pero que tarde más de lo esperado. Digamos que esta línea se vería asíEn lugar de ejecutar realmente ese comando, puedes hacer
La reversión al final permite ejecutar esto sin modificar realmente la base de datos, pero aún así obtienes el tiempo detallado de lo que tomó cuánto. Después de ejecutar eso, puede encontrar en la salida que algunos disparadores causan grandes demoras:
El valor
time
está en ms (milisegundos), por lo que la comprobación de esta restricción llevó unos 12,3 segundos. Debe agregar una nuevaINDEX
sobre las columnas requeridas para que este desencadenador se pueda calcular de manera efectiva. Para referencias de clave externa, la columna que hace referencia a otra tabla debe indexarse (es decir, la columna de origen, no la columna de destino). PostgreSQL no crea automáticamente dichos índices para usted yDELETE
es la única consulta común en la que realmente necesita ese índice. Como resultado, es posible que haya acumulado años de datos hasta llegar al caso dondeDELETE
es demasiado lento debido a la falta de un índice.Una vez que haya corregido el rendimiento de esa restricción (o alguna otra cosa que tomó demasiado tiempo), repita el comando en
begin
/rollback
block para que pueda comparar el nuevo tiempo de ejecución con el anterior. Continúe hasta que esté satisfecho con el tiempo de respuesta de eliminación de una sola línea (obtuve una consulta para pasar de 25.6 segundos a 15 ms simplemente agregando diferentes índices). Luego puede proceder a completar su eliminación completa sin ningún truco.(Tenga en cuenta que
EXPLAIN
necesita una consulta que pueda completarse con éxito. Una vez tuve un problema en el que PostgreSQL tardó demasiado en darse cuenta de que una eliminación violaría una restricción de clave externa y, en ese casoEXPLAIN
, no se puede usar porque no emitirá tiempo para fallar consultas. No conozco ninguna forma fácil de depurar problemas de rendimiento en tal caso).fuente
La desactivación de los desencadenantes puede ser una amenaza para la integridad de la base de datos y no se puede recomendar; sin embargo, si está seguro de que su operación es a prueba de fallas de restricciones, puede deshabilitar los desencadenantes, con lo siguiente:
SET session_replication_role = replica;
Ejecute el
DELETE
aquí.Para restaurar los desencadenantes, ejecute:
SET session_replication_role = DEFAULT;
Fuente aquí.
fuente
Si tiene activadores ON DELETE CASCADE, es de esperar que estén allí por algún motivo y, por lo tanto, no deberían deshabilitarse. Otro truco (todavía agrega tus índices) que funciona para mí es crear una función de eliminación que elimine manualmente los datos comenzando con las tablas al final de la cascada, y que trabaje hacia la tabla principal. (Esto es lo mismo que tendría que hacer si tuviera un activador ON DELETE RESTRICT)
En este caso, elimine los datos en tablec luego tableb luego tablea
fuente