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

Comprender el rendimiento de consultas de PostgreSQL

Averiguar por qué una consulta que funciona bien en desarrollo y pruebas explota en producción a veces puede ser un desafío. Continúe leyendo para obtener más información sobre algunas funciones que pueden proporcionar información sobre el rendimiento de sus consultas en producción.

Consultas actualmente en ejecución

Cuando un cliente se conecta a un servidor PostgreSQL, el proceso principal del servidor Postgres (históricamente denominado postmaster ) genera un nuevo proceso (llamado backend ) para atender las consultas del cliente. Cada backend, por lo tanto, está esperando a que su cliente envíe una consulta o intentando ejecutar una.

La vista del sistema pg_stat_activity muestra información sobre cada backend que se está ejecutando actualmente. En particular, muestra la consulta que el backend está ejecutando actualmente si está activa, o la última consulta que ejecutó si está esperando que el cliente envíe otra consulta.

Aquí hay dos backends que atienden a clientes conectados a la base de datos testdb , con ambos ejecutando activamente sus consultas:

testdb=# select usename,datname,state,query from pg_stat_activity where datname='testdb';
-[ RECORD 1 ]-----------------------------------------------------------------------------
usename | postgres
datname | testdb
state   | active
query   | SELECT pg_sleep(10);
-[ RECORD 2 ]-----------------------------------------------------------------------------
usename | postgres
datname | testdb
state   | active
query   | select usename,datname,state,query from pg_stat_activity where datname='testdb';

A veces, la consulta puede estar esperando un bloqueo, y esto también aparece en pg_stat_activity. Puede ver un INSERT esperando un bloqueo de relación aquí:

testdb=# select wait_event_type, wait_event, left(query, 60) from pg_stat_activity where datname='testdb';
-[ RECORD 1 ]---+-------------------------------------------------------------
wait_event_type | Client
wait_event      | ClientRead
left            | lock table t in access exclusive mode;
-[ RECORD 2 ]---+-------------------------------------------------------------
wait_event_type |
wait_event      |
left            | select wait_event_type, wait_event, left(query, 60) from pg_
-[ RECORD 3 ]---+-------------------------------------------------------------
wait_event_type | Lock
wait_event      | relation
left            | insert into t values (1);

Para obtener más información sobre pg_stat_activity, consulte los documentos.

Si bien esta vista es útil para comprender lo que Postgres está haciendo actualmente, no proporciona información sobre las estadísticas de ejecución de consultas ni sobre las consultas que terminaron de ejecutarse.

Todas las consultas ejecutadas en el pasado

Para eso, la extensión pg_stat_statements tiene un valor incalculable. Esta extensión está incluida en la distribución principal de PostgreSQL y también está disponible en servicios administrados como AWS RDS y GCP SQL.

pg_stat_statements (PSS) es una "extensión" en términos de PostgreSQL y debe instalarse primero:

  • Consulte la documentación de su distribución de Linux para ver si la extensión está preinstalada o si requiere la instalación de otro paquete. Por ejemplo, en Centos 7 deberá sudo yum install postgresql-contrib .
  • Edite el archivo de configuración principal postgresql.conf (normalmente en /etc , como /etc/postgresql/10/main/postgresql.conf en Debian) y cambie el valor de shared_preload_libraries a “pg_stat_statements”. Esta es una lista de valores separados por comas, por lo que si ya hay algo allí, agregue una coma y luego "pg_stat_statements".
  • Para AWS RDS, deberá modificar su grupo de parámetros activo y establecer el valor.
  • Después de editar "shared_preload_libraries", deberá reiniciar el demonio de PostgreSQL. Desafortunadamente, no hay forma de evitar esto. En AWS RDS, deberá reiniciar la instancia de RDS.
  • Después de reiniciar, el servidor PostgreSQL habría cargado la biblioteca compartida y podemos instalar la extensión ejecutando CREATE EXTENSION pg_stat_statements . Deberá ser superusuario para ejecutar este comando.
  • Puede instalar la extensión en cualquier base de datos y, sin embargo, ver las consultas en todas las bases de datos.

Una vez instalada la extensión, puede consultar la vista llamada pg_stat_statements para obtener información sobre cada una de las consultas ejecutadas desde que se instaló la extensión.

Los números, como el tiempo necesario para ejecutar la consulta, se acumulan como una suma. Solo para el tiempo de ejecución de la consulta, se presentan algunas estadísticas (promedio, mínimo, máximo, desviación estándar). Estos valores se pueden borrar usando la función pg_stat_statements_reset .

Así es como una fila de pg_stat_statements parece:

testdb=# select * from pg_stat_statements where query like '%pg_sleep%' and dbid=42548;
-[ RECORD 1 ]-------+--------------------
userid              | 10
dbid                | 42548
queryid             | 2649515222348904837
query               | SELECT pg_sleep($1)
calls               | 1
total_time          | 10016.782625
min_time            | 10016.782625
max_time            | 10016.782625
mean_time           | 10016.782625
stddev_time         | 0
rows                | 1
shared_blks_hit     | 0
shared_blks_read    | 0
shared_blks_dirtied | 0
shared_blks_written | 0
local_blks_hit      | 0
local_blks_read     | 0
local_blks_dirtied  | 0
local_blks_written  | 0
temp_blks_read      | 0
temp_blks_written   | 0
blk_read_time       | 0
blk_write_time      | 0

Aparte de los parámetros identificativos (usuario, base de datos, consulta), puedes averiguar muchas cosas interesantes sobre tu consulta:

  • Cuánto tiempo lleva ejecutar normalmente (mean_time )
  • Cuántas filas devuelve en promedio (rows / calls )
  • La cantidad de datos leídos del caché de búfer compartido y la cantidad de datos leídos del disco (el shared_blks_read muestra la cantidad total de datos que leyó la consulta, de los cuales shared_blks_hit vino del caché)
  • La cantidad de datos que se tuvieron que escribir en el disco sincrónicamente debido a la presión de la memoria caché (shared_blks_written )
  • La cantidad de datos escritos, como la cantidad de bloques tocados (shared_blks_dirtied )
  • La cantidad de tiempo dedicado a las lecturas y escrituras del disco (blk_{read,write}_time )
  • Archivos temporales escritos y leídos (temp_blks_{read,written} )
  • Tablas temporales escritas y leídas (local_* )

Los tiempos de lectura y escritura del disco están disponibles solo si el parámetro de configuración track_io_timing está prendido. Por defecto, no lo es. En la mayoría de los sistemas Linux modernos, debería estar bien activar este parámetro. Leer más.

Vale la pena tomar una instantánea de pg_stat_statements datos continuamente a intervalos regulares para ver la tendencia de estos parámetros por consulta. La herramienta de código abierto pgmetrics puede extraer y exponer las pg_stat_statements datos como JSON para facilitar la automatización.

Consultas ejecutadas durante un intervalo de tiempo

Una vez que haya implementado un sistema de este tipo, resultará fácil realizar un seguimiento de las consultas ejecutadas en un período de tiempo determinado. Esto facilita la depuración de problemas como por qué un trabajo por lotes nocturno tardó más de lo esperado.

Al restar los contadores entre dos marcas de tiempo dadas, puede encontrar la mayoría de los números como antes, excepto el mínimo, el máximo y la desviación estándar. Esto es suficiente para identificar las consultas que se ejecutaron dentro del rango de tiempo y los recursos que consumieron.

Registro de consultas lentas

Otra forma de identificar rápidamente las consultas que toman más tiempo del esperado es activar el registro de declaraciones. Puede especificar una duración de umbral y, si la consulta tarda más en finalizar, se registra. (En el archivo de registro regular de PostgreSQL, no hay uno separado para consultas lentas).

Para activar esta función, edite la configuración de la siguiente manera:

log_min_duration_statement = 1000 # in milliseconds

y recargar Postgres. También puedes usar ALTER SYSTEM :

ALTER SYSTEM SET log_min_duration_statement = 1000; -- in milliseconds

Con esto, se registra cualquier declaración (incluidas las que no sean DML) que tarde más de un segundo en finalizar:

2019-12-02 16:57:05.727 UTC [8040] postgres@testdb LOG:  duration: 10017.862 ms  statement: SELECT pg_sleep(10);

Se registra el tiempo real que tarda la consulta, así como el texto SQL completo.

Si tiene un sistema de monitoreo de registros y puede rastrear la cantidad de consultas lentas por hora / por día, puede servir como un buen indicador del rendimiento de la aplicación.

Planes de ejecución de consultas

Una vez que haya localizado una consulta que cree que debería ejecutarse más rápido, el siguiente paso es echar un vistazo a su plan de consulta. Por lo general, necesita el plan de consulta real de los servidores de producción para trabajar. Si puede ejecutar EXPLAIN en servidores de producción tan buenos, de lo contrario, debe confiar en auto_explain .

auto_explain es otra extensión central de PostgreSQL, ya sea instalada o disponible como un paquete de "contrib" para su distribución. También está disponible en AWSRDS. auto_explain es un poco más simple de instalar que pg_stat_statements :

  • Edite la configuración de postgres (o el grupo de parámetros RDS)shared_preload_libraries para incluir auto_explain .
  • Sin embargo, no es necesario que reinicie Postgres, puede simplemente ejecutar:LOAD 'auto_explain'; .
  • Querrá configurar sus ajustes, al menos este:
    • auto_explain.log_min_duration = 1000 # seconds

Básicamente, siempre que una consulta tarde más de auto_explain.log_min_duration número de segundos para completar, auto_explain registra la consulta y su plan de ejecución de consulta en el archivo de registro, así:

2019-12-04 09:23:05.130 UTC [12823] postgres@testdb LOG:  duration: 11025.765 ms  plan:
        Query Text: select pg_sleep(11);
        Result  (cost=0.00..0.01 rows=1 width=4) (actual time=11025.716..11025.718 rows=1 loops=1)
          Output: pg_sleep('11'::double precision)

También puede registrar el plan en formato JSON, si tiene scripts que puedan procesarlo:

2019-12-02 17:30:53.676 UTC [8040] postgres@testdb LOG:  duration: 10000.230 ms  plan:
        {
          "Query Text": "SELECT pg_sleep(10);",
          "Plan": {
            "Node Type": "Result",
            "Parallel Aware": false,
            "Startup Cost": 0.00,
            "Total Cost": 0.01,
            "Plan Rows": 1,
            "Plan Width": 4,
            "Actual Startup Time": 10000.205,
            "Actual Total Time": 10000.206,
            "Actual Rows": 1,
            "Actual Loops": 1,
            "Output": ["pg_sleep('10'::double precision)"],
            "Shared Hit Blocks": 0,
            "Shared Read Blocks": 0,
            "Shared Dirtied Blocks": 0,
            "Shared Written Blocks": 0,
            "Local Hit Blocks": 0,
            "Local Read Blocks": 0,
            "Local Dirtied Blocks": 0,
            "Local Written Blocks": 0,
            "Temp Read Blocks": 0,
            "Temp Written Blocks": 0,
            "I/O Read Time": 0.000,
            "I/O Write Time": 0.000
          },
          "Triggers": [
          ]
        }

En Postgres, no hay otra forma que no sea auto_explain para ver el plan de ejecución de una consulta que ya se ejecutó, lo que hace que auto_explain sea una herramienta importante en su caja de herramientas.