Esto ha surgido varias veces recientemente, tanto en SO como en las listas de correo de PostgreSQL.
El TL;DR para sus dos últimos puntos:
(a) Los shared_buffers más grandes pueden ser la razón por la que TRUNCATE es más lento en el servidor de CI. La configuración diferente de fsync o el uso de medios rotativos en lugar de SSD también podrían tener la culpa.
(b) TRUNCATE
tiene un costo fijo, pero no necesariamente más lento que DELETE
, además hace más trabajo. Consulte la explicación detallada a continuación.
ACTUALIZACIÓN: A partir de esta publicación surgió una discusión importante sobre el rendimiento de pgsql. Ver este hilo.
ACTUALIZACIÓN 2: Se han agregado mejoras a 9.2beta3 que deberían ayudar con esto, consulte esta publicación.
Explicación detallada de TRUNCATE
vs DELETE FROM
:
Si bien no soy un experto en el tema, entiendo que TRUNCATE
tiene un costo casi fijo por mesa, mientras que DELETE
es al menos O(n) para n filas; peor si hay claves externas que hacen referencia a la tabla que se está eliminando.
Siempre supuse que el costo fijo de un TRUNCATE
fue más bajo que el costo de un DELETE
en una mesa casi vacía, pero esto no es cierto en absoluto.
TRUNCATE table;
hace más que DELETE FROM table;
El estado de la base de datos después de una TRUNCATE table
es lo mismo que si en lugar de eso ejecutaras:
DELETE FROM table;
VACCUUM (FULL, ANALYZE) table;
(Solo 9.0+, ver nota al pie)
... aunque por supuesto TRUNCATE
en realidad no logra sus efectos con un DELETE
y un VACUUM
.
El punto es que DELETE
y TRUNCATE
hacer cosas diferentes, por lo que no solo está comparando dos comandos con resultados idénticos.
Un DELETE FROM table;
permite que permanezcan las filas muertas y la hinchazón, permite que los índices lleven entradas muertas, no actualiza las estadísticas de la tabla utilizadas por el planificador de consultas, etc.
Un TRUNCATE
le brinda una tabla e índices completamente nuevos como si fueran solo CREATE
edición Es como si hubiera eliminado todos los registros, vuelto a indexar la tabla e hizo un VACUUM FULL
.
Si no le importa si queda basura en la tabla porque está a punto de ir a llenarla de nuevo, es mejor que use DELETE FROM table;
.
Porque no estás ejecutando VACUUM
encontrará que las filas muertas y las entradas de índice se acumulan como un exceso que debe escanearse y luego ignorarse; esto ralentiza todas sus consultas. Si sus pruebas en realidad no crean y eliminan toda esa cantidad de datos, es posible que no lo note o no le importe, y siempre puede hacer un VACUUM
o dos a la mitad de su ejecución de prueba si lo hace. Mejor, deje que las configuraciones agresivas de autovacuum aseguren que autovacuum lo haga por usted en segundo plano.
Todavía puedes TRUNCATE
todas sus tablas después del todo se ejecuta el conjunto de pruebas para asegurarse de que no se acumulen efectos en muchas ejecuciones. En 9.0 y posteriores, VACUUM (FULL, ANALYZE) table;
globalmente sobre la mesa es al menos igual de bueno, si no mejor, y es mucho más fácil.
IIRC Pg tiene algunas optimizaciones que significan que puede notar cuándo su transacción es la única que puede ver la tabla e inmediatamente marcar los bloques como libres de todos modos. En las pruebas, cuando quise crear una gran cantidad, tuve que tener más de una conexión simultánea para hacerlo. Sin embargo, no confiaría en esto.
DELETE FROM table;
es muy barato para mesas pequeñas sin referencias f/k
Para DELETE
todos los registros de una tabla sin referencias de clave externa, todo Pg tiene que hacer un escaneo de tabla secuencial y establecer el xmax
de las tuplas encontradas. Esta es una operación muy económica, básicamente una lectura lineal y una escritura semilineal. AFAIK no tiene que tocar los índices; continúan apuntando a las tuplas muertas hasta que son limpiadas por un VACUUM
posterior eso también marca bloques en la tabla que contienen solo tuplas muertas como libres.
DELETE
solo se vuelve caro si hay muchos de registros, si hay muchas referencias de claves foráneas que deben verificarse, o si cuenta la siguiente tabla VACUUM (FULL, ANALYZE) table;
necesario para coincidir con TRUNCATE
efectos de dentro del costo de su DELETE
.
En mis pruebas aquí, un DELETE FROM table;
normalmente era 4 veces más rápido que TRUNCATE
a 0,5 ms frente a 2 ms. Esa es una base de datos de prueba en un SSD, ejecutándose con fsync=off
porque no me importa si pierdo todos estos datos. Por supuesto, DELETE FROM table;
no está haciendo el mismo trabajo, y si sigo con una tabla VACUUM (FULL, ANALYZE) table;
son 21 ms mucho más caros, por lo que DELETE
solo es una victoria si no necesito que la mesa esté impecable.
TRUNCATE table;
hace mucho más trabajo de costo fijo y limpieza que DELETE
Por el contrario, un TRUNCATE
tiene que hacer mucho trabajo. Debe asignar nuevos archivos para la tabla, su tabla TOAST, si la hay, y todos los índices que tiene la tabla. Los encabezados deben escribirse en esos archivos y es posible que los catálogos del sistema también deban actualizarse (no estoy seguro de ese punto, no lo he comprobado). Luego tiene que reemplazar los archivos antiguos con los nuevos o eliminar los antiguos, y tiene que asegurarse de que el sistema de archivos se haya puesto al día con los cambios con una operación de sincronización, fsync() o similar, que generalmente vacía todos los búferes en el disco. . No estoy seguro de si la sincronización se omite si está ejecutando con la opción (alimentación de datos) fsync=off
.
Recientemente aprendí que TRUNCATE
también debe vaciar todos los búfer de PostgreSQL relacionados con la tabla anterior. Esto puede tomar una cantidad de tiempo no trivial con grandes shared_buffers
. Sospecho que es por eso que es más lento en su servidor CI.
El equilibrio
De todos modos, puedes ver que un TRUNCATE
de una tabla que tiene una tabla TOAST asociada (la mayoría la tiene) y varios índices podría tardar unos minutos. No mucho, pero más que un DELETE
de una mesa casi vacía.
En consecuencia, es mejor que hagas DELETE FROM table;
.
--
Nota:en bases de datos anteriores a 9.0, CLUSTER table_id_seq ON table; ANALYZE table;
o tabla VACUUM FULL ANALYZE table; REINDEX table;
sería un equivalente más cercano a TRUNCATE
. El VACUUM FULL
impl cambió a uno mucho mejor en 9.0.