sql >> Base de Datos >  >> RDS >> PostgreSQL

Velocidad de truncamiento de Postgresql

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.