Autovacuum agresivo en PostgreSQL

42

Estoy tratando de hacer que PostgreSQL aspire mi base de datos de forma agresiva. Actualmente he configurado el vacío automático de la siguiente manera:

  • autovacuum_vacuum_cost_delay = 0 # Apague el vacío basado en costos
  • autovacuum_vacuum_cost_limit = 10000 # Valor máximo
  • autovacuum_vacuum_threshold = 50 # Valor predeterminado
  • autovacuum_vacuum_scale_factor = 0.2 # Valor predeterminado

Noté que el vacío automático solo se activa cuando la base de datos no está bajo carga, por lo que me meto en situaciones en las que hay muchas más tuplas muertas que tuplas vivas. Vea la captura de pantalla adjunta para ver un ejemplo. Una de las mesas tiene 23 tuplas vivas, pero 16845 tuplas muertas en espera de vacío. ¡Eso es una locura!

Muchas tuplas muertas

El vacío automático se activa cuando finaliza la ejecución de la prueba y el servidor de la base de datos está inactivo, que no es lo que quiero, ya que me gustaría que se activara el vacío automático cada vez que el número de tuplas muertas supere el 20% de tuplas vivas + 50, ya que la base de datos ha sido configurado El vacío automático cuando el servidor está inactivo es inútil para mí, ya que se espera que el servidor de producción alcance miles de actualizaciones / segundo durante un período prolongado, por lo que necesito el vacío automático para funcionar incluso cuando el servidor está bajo carga.

¿Hay algo que me falta? ¿Cómo fuerzo el vacío automático para que se ejecute mientras el servidor está bajo una carga pesada?

Actualizar

¿Podría ser esto un problema de bloqueo? Las tablas en cuestión son tablas de resumen que se completan mediante un desencadenador de inserción posterior. Estas tablas están bloqueadas en el modo COMPARTIR EXCLUSIVA DE LA FILA para evitar escrituras concurrentes en la misma fila.

CadentOrange
fuente

Respuestas:

40

Es casi seguro que Eelke está en lo cierto de que su bloqueo está bloqueando el vacío automático. Autovacuum está diseñado para dar paso a la actividad del usuario, deliberadamente. Si esas tablas están bloqueadas, el vacío automático no puede aspirarlas.

Sin embargo, para la posteridad, quería dar un conjunto de configuraciones de ejemplo para el vacío automático hiper-agresivo, ya que las configuraciones que proporcionó no lo hacen. Sin embargo, tenga en cuenta que hacer que el autovacuum sea más agresivo probablemente no resuelva su problema. También tenga en cuenta que la configuración predeterminada de vacío automático se basa en ejecutar más de 200 ejecuciones de prueba utilizando DBT2 buscando una combinación óptima de configuraciones, por lo que se debe suponer que los valores predeterminados son buenos a menos que tenga una razón sólida para pensar lo contrario, o a menos que su base de datos esté significativamente fuera la corriente principal para bases de datos OLTP (por ejemplo, una pequeña base de datos que recibe actualizaciones de 10K por segundo, o un almacén de datos de 3TB).

Primero, active el registro para poder verificar si el autovacuum está haciendo lo que cree que es:

log_autovacuum_min_duration = 0

Luego, hagamos más trabajadores de autovac y hagamos que revisen las tablas con más frecuencia:

autovacuum_max_workers = 6
autovacuum_naptime = 15s

Bajemos los umbrales para el vacío automático y el análisis automático para que se activen antes:

autovacuum_vacuum_threshold = 25
autovacuum_vacuum_scale_factor = 0.1

autovacuum_analyze_threshold = 10
autovacuum_analyze_scale_factor = 0.05 

Luego, hagamos que el vacío automático sea menos interrumpible, para que se complete más rápido, pero a costa de tener un mayor impacto en la actividad concurrente del usuario:

autovacuum_vacuum_cost_delay = 10ms
autovacuum_vacuum_cost_limit = 1000

Existe su programa completo para autovacuum genéricamente agresivo, que puede ser apropiado para una pequeña base de datos que obtiene una tasa muy alta de actualizaciones, pero puede tener un impacto demasiado grande en la actividad concurrente del usuario.

Además, tenga en cuenta que los parámetros de autovacuum se pueden ajustar por tabla , lo que casi siempre es una mejor respuesta para la necesidad de ajustar el comportamiento de autovacuum.

De nuevo, sin embargo, es poco probable que aborde su problema real.

Josh Berkus
fuente
36

Solo para ver qué tablas califican para autovacuum, se puede usar la siguiente consulta (basada en http://www.postgresql.org/docs/current/static/routine-vacuuming.html ). Sin embargo, tenga en cuenta que la consulta no busca configuraciones específicas de la tabla:

 SELECT psut.relname,
     to_char(psut.last_vacuum, 'YYYY-MM-DD HH24:MI') as last_vacuum,
     to_char(psut.last_autovacuum, 'YYYY-MM-DD HH24:MI') as last_autovacuum,
     to_char(pg_class.reltuples, '9G999G999G999') AS n_tup,
     to_char(psut.n_dead_tup, '9G999G999G999') AS dead_tup,
     to_char(CAST(current_setting('autovacuum_vacuum_threshold') AS bigint)
         + (CAST(current_setting('autovacuum_vacuum_scale_factor') AS numeric)
            * pg_class.reltuples), '9G999G999G999') AS av_threshold,
     CASE
         WHEN CAST(current_setting('autovacuum_vacuum_threshold') AS bigint)
             + (CAST(current_setting('autovacuum_vacuum_scale_factor') AS numeric)
                * pg_class.reltuples) < psut.n_dead_tup
         THEN '*'
         ELSE ''
     END AS expect_av
 FROM pg_stat_user_tables psut
     JOIN pg_class on psut.relid = pg_class.oid
 ORDER BY 1;
pygrac
fuente
11

Sí, es un problema de bloqueo. De acuerdo con esta página (no llena), VACUUM necesita acceso exclusivo para COMPARTIR ACTUALIZACIÓN que está bloqueado por el nivel de bloqueo que está utilizando.

¿Estás seguro de que necesitas esta cerradura? PostgreSQL es compatible con ACID, por lo que las escrituras concurrentes en la mayoría de los casos no son un problema, ya que PostgreSQL abortará una de las transacciones si ocurriera una violación de serialización.

También puede bloquear filas utilizando SELECCIONAR PARA ACTUALIZAR para bloquear filas en lugar de toda la tabla.

Otra alternativa sin bloqueo sería utilizar el nivel de aislamiento de transacción serializable. Sin embargo, esto podría afectar el rendimiento de otras transacciones y debe estar preparado para más fallas de serialización.

Eelke
fuente
Esto se debe al bloqueo de las tablas de resumen, ya que estas se bloquean utilizando el MODO EXCLUSIVO COMPARTIR FILA. Las escrituras concurrentes sin el bloqueo pueden tener éxito, pero seguramente terminarán con valores incorrectos. Imagine que estoy manteniendo un recuento N de filas de tipo X. Si inserto simultáneamente 2 filas de tipo X, sin bloquear terminaré con N + 1 en mi tabla de resumen en lugar de N + 2. La solución que he adoptado es tener un trabajo cron que aspira manualmente las tablas de resumen en mi base de datos. Funciona bien y parece ser el enfoque recomendado, pero me parece un truco.
CadentOrange
6

Aumentar la cantidad de procesos de autovacío y reducir la siesta probablemente ayudará. Aquí está la configuración para un PostgreSQL 9.1 que uso en un servidor que almacena información de respaldo y, como resultado, obtiene mucha actividad de inserción.

http://www.postgresql.org/docs/current/static/runtime-config-autovacuum.html

autovacuum_max_workers = 6              # max number of autovacuum subprocesses
autovacuum_naptime = 10         # time between autovacuum runs
autovacuum_vacuum_cost_delay = 20ms     # default vacuum cost delay for

También intentaré bajar el nivel cost_delaypara que la aspiradora sea más agresiva.

También puedo probar el autovacuuming usando pgbench.

http://wiki.postgresql.org/wiki/Pgbenchtesting

Ejemplo de alta contención:

Crear una base de datos bench_replication

pgbench -i -p 5433 bench_replication

Ejecute pgbench

pgbench -U postgres -p 5432 -c 64 -j 4 -T 600 bench_replication

Verificar el estado de aspiración automática

psql
>\connect bench_replicaiton
bench_replication=# select schemaname, relname, last_autovacuum from pg_stat_user_tables;
 schemaname |     relname      |        last_autovacuum        
------------+------------------+-------------------------------
 public     | pgbench_branches | 2012-07-18 18:15:34.494932+02
 public     | pgbench_history  | 
 public     | pgbench_tellers  | 2012-07-18 18:14:06.526437+02
 public     | pgbench_accounts | 
Craig Efrein
fuente
6

El script "calificar para autovacuum" existente es muy útil, pero (como se indicó correctamente) faltaban opciones específicas de la tabla. Aquí hay una versión modificada que tiene en cuenta esas opciones:

WITH rel_set AS
(
    SELECT
        oid,
        CASE split_part(split_part(array_to_string(reloptions, ','), 'autovacuum_vacuum_threshold=', 2), ',', 1)
            WHEN '' THEN NULL
        ELSE split_part(split_part(array_to_string(reloptions, ','), 'autovacuum_vacuum_threshold=', 2), ',', 1)::BIGINT
        END AS rel_av_vac_threshold,
        CASE split_part(split_part(array_to_string(reloptions, ','), 'autovacuum_vacuum_scale_factor=', 2), ',', 1)
            WHEN '' THEN NULL
        ELSE split_part(split_part(array_to_string(reloptions, ','), 'autovacuum_vacuum_scale_factor=', 2), ',', 1)::NUMERIC
        END AS rel_av_vac_scale_factor
    FROM pg_class
) 
SELECT
    PSUT.relname,
    to_char(PSUT.last_vacuum, 'YYYY-MM-DD HH24:MI')     AS last_vacuum,
    to_char(PSUT.last_autovacuum, 'YYYY-MM-DD HH24:MI') AS last_autovacuum,
    to_char(C.reltuples, '9G999G999G999')               AS n_tup,
    to_char(PSUT.n_dead_tup, '9G999G999G999')           AS dead_tup,
    to_char(coalesce(RS.rel_av_vac_threshold, current_setting('autovacuum_vacuum_threshold')::BIGINT) + coalesce(RS.rel_av_vac_scale_factor, current_setting('autovacuum_vacuum_scale_factor')::NUMERIC) * C.reltuples, '9G999G999G999') AS av_threshold,
    CASE
        WHEN (coalesce(RS.rel_av_vac_threshold, current_setting('autovacuum_vacuum_threshold')::BIGINT) + coalesce(RS.rel_av_vac_scale_factor, current_setting('autovacuum_vacuum_scale_factor')::NUMERIC) * C.reltuples) < PSUT.n_dead_tup
        THEN '*'
    ELSE ''
    END AS expect_av
FROM
    pg_stat_user_tables PSUT
    JOIN pg_class C
        ON PSUT.relid = C.oid
    JOIN rel_set RS
        ON PSUT.relid = RS.oid
ORDER BY C.reltuples DESC;
Vadim Zingertal
fuente