PostgreSQL viene con una plétora de opciones de configuración, pero cambiar la configuración predeterminada de algunas de estas opciones mejora drásticamente la observabilidad de su servidor PostgreSQL. Querrá establecer y configurar estas opciones antes de que surjan problemas en producción, ya que pueden proporcionar información esencial para comprender y resolver esos problemas.
Siga leyendo para obtener más información sobre la configuración y las extensiones que exponen métricas e información sobre el funcionamiento interno de su servidor PostgreSQL.
Prefijo de línea de registro
El prefijo_línea_registro La opción de configuración determina lo que escribe PostgreSQL al comienzo de cada línea de registro. El valor predeterminado depende de la distribución específica de Linux o la solución administrada que esté utilizando, pero la mayoría de las veces no incluye algunos elementos que pueden resultar muy útiles para rastrear a los clientes que se comportan mal. Prueba este log_line_prefix :
log_line_prefix = '%m [%p] %a %u %d %h '
Incluye la marca de tiempo (%m ), el PID del proceso backend (%p ), el nombre de la aplicación (%a ) del cliente, el nombre de usuario con el que se ha conectado el cliente (%u ), la base de datos a la que se ha conectado el cliente (%d ) y el nombre de host o IP de donde proviene la conexión (%h ). Esto da como resultado loglines como este:
2021-01-30 05:06:03.675 UTC [73] psql postgres bench 172.17.0.1 ERROR: relation "pgbench_akkounts" does not exist at character 15
2021-01-30 05:06:03.675 UTC [73] psql postgres bench 172.17.0.1 STATEMENT: select * from pgbench_akkounts;
que son mucho más útiles que los predeterminados. Puede ver que un cliente se conectó desde 172.17.0.1 como usuario postgres a la base de datos bench y la aplicación era psql . Definitivamente una mejora sobre la opción predeterminada, que muestra solo esto:
2021-01-30 05:13:22.630 UTC [63] ERROR: relation "pgbench_akkounts" does not exist at character 15
2021-01-30 05:13:22.630 UTC [63] STATEMENT: select * from pgbench_akkounts;
Registro de consultas lentas
PostgreSQL se puede configurar para registrar consultas que tardan más de un tiempo determinado en ejecutarse. Estos van en el mismo archivo de registro; no hay un archivo de registro de consultas lentas separado como en MySQL.
Para registrar instrucciones que tardan más de 1 segundo en ejecutarse, use log_min_duration_statement opción como esta:
log_min_duration_statement = 1s
Tenga en cuenta que log_min_duration_statement considerará todas las declaraciones (incluidas, por ejemplo, las declaraciones de administrador de ejecución prolongada como REINDEX TABLE ) y no solo consultas (SELECT ). Aquí hay algunas entradas de registro producidas por esta opción:
2021-01-30 08:42:57.473 UTC [62] psql postgres postgres 172.17.0.1 LOG: duration: 1016.283 ms statement: select pg_sleep(1);
2021-01-30 08:52:00.541 UTC [62] psql postgres postgres 172.17.0.1 LOG: duration: 1118.277 ms statement: select pg_sleep(1.1);
Si esto da como resultado demasiados registros de declaraciones similares, puede decirle a Postgres que registre solo un porcentaje, usando:
log_min_duration_statement = -1
log_min_duration_sample = 1s
log_statement_sample_rate = 0.25
Esto registra solo el 25% de las declaraciones que se vuelven elegibles para el registro (las que tardaron más de 1 segundo en ejecutarse). La salida del registro es la misma que antes. No hay forma de saber cuántas declaraciones elegibles no se registraron.
Para registrar todas las declaraciones, junto con el tiempo necesario para ejecutarlas, use log_statement opción en su lugar:
log_statement = mod
log_duration = on
La opción 'mod' le dice a Postgres que registre DDL y declaraciones de modificación de datos. Esto da como resultado registros como estos:
2021-01-30 08:35:08.985 UTC [64] pgbench postgres bench 172.17.0.1 LOG: statement: insert into pgbench_tellers(tid,bid,tbalance) values (10,1,0)
2021-01-30 08:35:08.985 UTC [64] pgbench postgres bench 172.17.0.1 LOG: duration: 0.241 ms
Tenga cuidado de que no Si es posible probar el registro de declaraciones habilitado de esta manera, todas las declaraciones se registrarán y terminará con toneladas de entradas de registro.
Registro de bloqueos e interbloqueos
Las consultas pueden esperar demasiado para adquirir un bloqueo. Por lo general, se establece un límite superior sobre el tiempo de espera mediante la opción lock_timeout , generalmente en el lado del cliente. Si una consulta ha estado esperando tanto tiempo para adquirir un bloqueo, Postgres cancelará la ejecución de esta consulta y registrará un error:
2021-01-30 09:35:52.415 UTC [67] psql postgres testdb 172.17.0.1 ERROR: canceling statement due to lock timeout
2021-01-30 09:35:52.415 UTC [67] psql postgres testdb 172.17.0.1 STATEMENT: cluster t;
Supongamos que desea establecer un tiempo de espera de bloqueo de 1 minuto, pero registrar consultas que esperan bloqueos durante más de, digamos, 30 segundos. Puedes hacer esto usando:
log_lock_waits = on
deadlock_timeout = 30s
Esto creará registros como este:
2021-01-30 09:49:22.331 UTC [70] psql postgres testdb 172.17.0.1 LOG: process 70 still waiting for ShareLock on transaction 493 after 30009.004 ms
2021-01-30 09:49:22.331 UTC [70] psql postgres testdb 172.17.0.1 DETAIL: Process holding the lock: 68. Wait queue: 70.
2021-01-30 09:49:22.331 UTC [70] psql postgres testdb 172.17.0.1 CONTEXT: while locking tuple (0,3) in relation "t"
2021-01-30 09:49:22.331 UTC [70] psql postgres testdb 172.17.0.1 STATEMENT: select * from t for update;
El uso de deadlock_timeout no es un error tipográfico:es el valor que utiliza el mecanismo de registro de espera de bloqueo. Idealmente, debería haber algo como log_min_duration_lock_wait ,pero desafortunadamente, ese no es el caso.
En caso de interbloqueos reales, Postgres anulará las transacciones interbloqueadas después de deadlock_timeout duración, y registrará las declaraciones infractoras. No es necesaria ninguna configuración explícita.
2021-01-30 09:55:37.724 UTC [68] psql postgres testdb 172.17.0.1 LOG: process 68 detected deadlock while waiting for ShareLock on transaction 496 after 30007.633 ms
2021-01-30 09:55:37.724 UTC [68] psql postgres testdb 172.17.0.1 DETAIL: Process holding the lock: 70. Wait queue: .
2021-01-30 09:55:37.724 UTC [68] psql postgres testdb 172.17.0.1 CONTEXT: while locking tuple (0,3) in relation "t"
2021-01-30 09:55:37.724 UTC [68] psql postgres testdb 172.17.0.1 STATEMENT: select * from t where a=4 for update;
2021-01-30 09:55:37.725 UTC [68] psql postgres testdb 172.17.0.1 ERROR: deadlock detected
2021-01-30 09:55:37.725 UTC [68] psql postgres testdb 172.17.0.1 DETAIL: Process 68 waits for ShareLock on transaction 496; blocked by process 70.
Process 70 waits for ShareLock on transaction 495; blocked by process 68.
Process 68: select * from t where a=4 for update;
Process 70: select * from t where a=0 for update;
2021-01-30 09:55:37.725 UTC [68] psql postgres testdb 172.17.0.1 HINT: See server log for query details.
2021-01-30 09:55:37.725 UTC [68] psql postgres testdb 172.17.0.1 CONTEXT: while locking tuple (0,3) in relation "t"
2021-01-30 09:55:37.725 UTC [68] psql postgres testdb 172.17.0.1 STATEMENT: select * from t where a=4 for update;
Registro de autovacíos
El proceso de vacío automático se activa cuando Postgres determina que los datos en una tabla han cambiado lo suficiente como para justificar un vacío y análisis. Para vigilar este proceso, habilite el registro de ejecuciones de vacío automático:
log_autovacuum_min_duration = 250ms
Aquí hay una entrada de muestra que fue causada por cambios excesivos en una tabla:
2021-01-30 10:23:33.201 UTC [63] LOG: automatic vacuum of table "postgres.public.t": index scans: 0
pages: 0 removed, 95 remain, 0 skipped due to pins, 0 skipped frozen
tuples: 8991 removed, 10000 remain, 0 are dead but not yet removable, oldest xmin: 492
buffer usage: 215 hits, 4 misses, 4 dirtied
avg read rate: 1.885 MB/s, avg write rate: 1.885 MB/s
system usage: CPU: user: 0.01 s, system: 0.00 s, elapsed: 0.01 s
WAL usage: 244 records, 1 full page images, 67984 bytes
2021-01-30 10:23:33.222 UTC [63] LOG: automatic analyze of table "postgres.public.t" system usage: CPU: user: 0.01 s, system: 0.00 s, elapsed: 0.01 s
Tenga en cuenta que el vacío automático generalmente activará un análisis después del vacío, y esto también se registrará.
Estos registros lo ayudarán a determinar la mejor manera de ajustar los parámetros de autovacío y lo ayudarán a investigar si el autovacío no está siendo tan efectivo como pensaba y cuándo lo sería.
Registro de puntos de control
La creación de puntos de control es el proceso de insertar los cambios registrados en WAL en los archivos reales que respaldan las tablas. Idealmente, los puntos de control deberían ocurrir en intervalos regulares y no demasiado frecuentes, ya que es un proceso intensivo de CPU y disco. Por varias razones, los puntos de control también se ven obligados a ocurrir antes de la siguiente hora programada, y esto da como resultado un rendimiento de consulta reducido.
Para controlar la frecuencia y la eficiencia de los puntos de control, habilite el registro de puntos de control:
log_checkpoints = on
Esto le dice a PostgreSQL que registre lo siguiente siempre que ocurra un punto de control:
2021-01-30 10:05:57.085 UTC [56] LOG: checkpoint starting: immediate force wait
2021-01-30 10:05:57.159 UTC [56] LOG: checkpoint complete: wrote 0 buffers (0.0%); 0 WAL file(s) added, 0 removed, 0 recycled; write=0.000 s, sync=0.000 s, total=0.074 s; sync files=0, longest=0.000 s, average=0.000 s; distance=0 kB, estimate=0 kB
La primera línea contiene las banderas que el backend pasó al punto de control. Puede ver que la "fuerza" provocó un punto de control a pesar de que no había cambios pendientes en el punto de control. Si no se hubiera especificado "inmediato", el punto de control habría apuntado hasta checkpoint_completion_target .
Otras configuraciones del lado del servidor
Hay un par de otras configuraciones que puede activar en su configuración de PostgreSQL que lo ayudarán a diagnosticar problemas:
- track_io_timing - configurando esto en on le permite ver el tiempo empleado en E/S del disco para cada consulta (combinado con la extensión pg_stat_statements descrita a continuación). Consulte los documentos sobre una advertencia para activar esto, pero debería ser seguro en casi cualquier Linux moderno. Es imposible ver el costo de E/S del disco de una consulta sin activar esta opción.
- track_commit_timestamp - configurando esto en on puede ser útil para depurar retrasos en la replicación y otros problemas relacionados con la replicación.
Estadísticas de consulta a través de pg_stat_statements
La extensión pg_stat_statements es un accesorio esencial para cualquier implementación de PostgreSQL. Recopila y registra estadísticas para cada consulta ejecutada y las presenta como una vista llamada "pg_stat_statements". Esta es una extensión, lo que significa que debe instalarla explícitamente en cada base de datos para la que desea datos, usando el comando:
CREATE EXTENSION pg_stat_statements;
Dado que la extensión se basa en un .so , deberás cargarlo usando shared_preload_libraries :
shared_preload_libraries = 'pg_stat_statements'
Desafortunadamente, esto requiere reiniciar el servidor PostgreSQL; así que asegúrese de hacer esto antes de lanzarlo.
Si actualizó desde una versión anterior de PostgreSQL, asegúrese de actualizar también su extensión pg_stat_statement, usando:
ALTER EXTENSION pg_stat_statements UPDATE;
La extensión pg_stat_statements no registra nada, se usa consultando la vista del mismo nombre. Para obtener más detalles, consulte la documentación oficial.
Planes de ejecución de consultas a través de auto_explain
explicación_automática es otra extensión presente en el núcleo de PostgreSQL. Puede registrar los planes de ejecución de consultas lentas. Solo necesita agregarse a shared_preload_libraries , y no necesita instalarse como una extensión. También tiene un par de otras opciones que normalmente deben establecerse en valores no predeterminados:
shared_preload_libraries = 'pg_stat_statements,auto_explain'
auto_explain.log_min_duration = 1s
auto_explain.log_analyze = on
auto_explain.log_buffers = on
auto_explain.log_triggers = on
auto_explain.log_timing = on
auto_explain.log_verbose = on
auto_explain.log_format = json
Lo anterior registra el plan de ejecución para cualquier consulta que tarde más de 1 segundo en completarse. Aquí hay una salida de muestra:
2021-01-30 11:28:25.977 UTC [64] psql postgres postgres 172.17.0.1 LOG: duration: 1.305 ms plan:
{
"Query Text": "SELECT n.nspname as \"Schema\",\n c.relname as \"Name\",\n CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' WHEN 'm' THEN 'materialized view' WHEN 'i' THEN 'index' WHEN 'S' TH
EN 'sequence' WHEN 's' THEN 'special' WHEN 'f' THEN 'foreign table' WHEN 'p' THEN 'table' WHEN 'I' THEN 'index' END as \"Type\",\n pg_catalog.pg_get_userbyid(c.relowner) as \"Owner\"\nFROM pg_catalog.pg_class c
\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\nWHERE c.relkind IN ('r','p','v','m','S','f','')\n AND n.nspname <> 'pg_catalog'\n AND n.nspname <> 'information_schema'\n AND
n.nspname !~ '^pg_toast'\n AND pg_catalog.pg_table_is_visible(c.oid)\nORDER BY 1,2;",
"Plan": {
"Node Type": "Sort",
"Parallel Aware": false,
"Startup Cost": 32.93,
"Total Cost": 33.01,
"Plan Rows": 32,
"Plan Width": 224,
"Actual Startup Time": 1.292,
"Actual Total Time": 1.298,
"Actual Rows": 0,
[... lots of text snipped ...]
Para obtener más información sobre auto_explain, consulte los documentos oficiales.
Las extensiones pg_stat_statements y explicación automática son las únicas dos opciones ampliamente admitidas que tiene PostgreSQL para la gestión del rendimiento de consultas y la gestión del plan de consultas. Vale la pena conocer estas dos funciones y planificar con anticipación su uso en producción.
Nombre de la aplicación
El nombre de la aplicación es un parámetro del lado del cliente y, por lo general, se puede establecer en cadenas de conexión de estilo DSN o libpq que su aplicación utiliza para la información de conexión. Muchas herramientas y utilidades en el sistema ecológico de PostgreSQL entienden el nombre de la aplicación y es útil establecerlo en un valor significativo, por ejemplo:
application_name = weekly-revenue-report
Esto se establecería para cada aplicación cliente que se conecte a su servidor PostgreSQL.