El módulo pg_stat_statements proporciona un medio para
registrar las estadísticas de planificación y ejecución de todas las sentencias SQL ejecutadas por
un servidor.
El módulo debe cargarse añadiendo pg_stat_statements a
shared_preload_libraries en
postgresql.conf, ya que requiere memoria compartida adicional.
Esto significa que se necesita reiniciar el servidor para añadir o eliminar el módulo.
Además, el cálculo del identificador de consulta debe estar habilitado para que el
módulo esté activo, lo cual se hace automáticamente si compute_query_id
está configurado en auto u on, o si se carga cualquier
módulo de terceros que calcule identificadores de consultas.
Cuando pg_stat_statements está activo, realiza el seguimiento de las
estadísticas en todas las bases de datos del servidor. Para acceder y manipular
estas estadísticas, el módulo proporciona las vistas
pg_stat_statements y
pg_stat_statements_info,
y las funciones de utilidad pg_stat_statements_reset y
pg_stat_statements. Estas no están disponibles globalmente, pero
se pueden habilitar para una base de datos específica con
CREATE EXTENSION pg_stat_statements.
pg_stat_statements #
Las estadísticas recopiladas por el módulo están disponibles a través de una
vista llamada pg_stat_statements. Esta vista
contiene una fila para cada combinación distinta de ID de base de datos, ID de usuario,
ID de consulta y si se trata de una sentencia de nivel superior o no (hasta
el número máximo de sentencias distintas que el módulo puede registrar).
Las columnas de la vista se muestran en la
Table F.22.
Table F.22. Columnas de pg_stat_statements
Columna Tipo Descripción |
|---|
OID del usuario que ejecutó la sentencia |
OID de la base de datos en la que se ejecutó la sentencia |
Verdadero si la consulta se ejecutó como una sentencia de nivel superior
(siempre verdadero si |
Código hash para identificar consultas normalizadas idénticas. |
Texto de una sentencia representativa |
Número de veces que se planificó la sentencia
(si |
Tiempo total dedicado a planificar la sentencia, en milisegundos
(si |
Tiempo mínimo dedicado a planificar la sentencia, en milisegundos.
Este campo será cero si |
Tiempo máximo dedicado a planificar la sentencia, en milisegundos.
Este campo será cero si |
Tiempo medio dedicado a planificar la sentencia, en milisegundos
(si |
Desviación estándar de la población del tiempo dedicado a planificar la sentencia,
en milisegundos
(si |
Número de veces que se ejecutó la sentencia |
Tiempo total dedicado a ejecutar la sentencia, en milisegundos |
Tiempo mínimo dedicado a ejecutar la sentencia, en milisegundos.
Este campo será cero hasta que esta sentencia se ejecute por primera vez
después de un restablecimiento realizado por la función
|
Tiempo máximo dedicado a ejecutar la sentencia, en milisegundos.
Este campo será cero hasta que esta sentencia se ejecute por primera vez
después de un restablecimiento realizado por la función
|
Tiempo medio dedicado a ejecutar la sentencia, en milisegundos |
Desviación estándar de la población del tiempo dedicado a ejecutar la sentencia, en milisegundos |
Número total de filas recuperadas o afectadas por la sentencia |
Número total de aciertos en la caché de bloques compartidos (shared block cache hits) por la sentencia |
Número total de bloques compartidos leídos por la sentencia |
Número total de bloques compartidos marcados como sucios (dirtied) por la sentencia |
Número total de bloques compartidos escritos por la sentencia |
Número total de aciertos en la caché de bloques locales por la sentencia |
Número total de bloques locales leídos por la sentencia |
Número total de bloques locales marcados como sucios por la sentencia |
Número total de bloques locales escritos por la sentencia |
Número total de bloques temporales leídos por la sentencia |
Número total de bloques temporales escritos por la sentencia |
Tiempo total que la sentencia dedicó a leer bloques compartidos, en milisegundos (si track_io_timing está habilitado; de lo contrario, cero) |
Tiempo total que la sentencia dedicó a escribir bloques compartidos, en milisegundos (si track_io_timing está habilitado; de lo contrario, cero) |
Tiempo total que la sentencia dedicó a leer bloques locales, en milisegundos (si track_io_timing está habilitado; de lo contrario, cero) |
Tiempo total que la sentencia dedicó a escribir bloques locales, en milisegundos (si track_io_timing está habilitado; de lo contrario, cero) |
Tiempo total que la sentencia dedicó a leer bloques de archivos temporales, en milisegundos (si track_io_timing está habilitado; de lo contrario, cero) |
Tiempo total que la sentencia dedicó a escribir bloques de archivos temporales, en milisegundos (si track_io_timing está habilitado; de lo contrario, cero) |
Número total de registros WAL generados por la sentencia |
Número total de imágenes de página completa WAL (WAL full page images) generadas por la sentencia |
Cantidad total de WAL generado por la sentencia en bytes |
Número de veces que los búferes WAL se llenaron por completo |
Número total de funciones JIT-compiladas por la sentencia |
Tiempo total dedicado por la sentencia a generar código JIT, en milisegundos |
Número de veces que las funciones han sido integradas en línea (inlined) |
Tiempo total dedicado por la sentencia a la integración en línea de funciones, en milisegundos |
Número de veces que la sentencia ha sido optimizada |
Tiempo total dedicado por la sentencia a la optimización, en milisegundos |
Número de veces que se ha emitido código |
Tiempo total dedicado por la sentencia a emitir código, en milisegundos |
Número total de funciones de deformación de tuplas (tuple deform functions) JIT-compiladas por la sentencia |
Tiempo total dedicado por la sentencia a la compilación JIT de funciones de deformación de tuplas, en milisegundos |
Número de trabajadores paralelos planificados para ser iniciados |
Número de trabajadores paralelos realmente iniciados |
Momento en el que comenzó la recopilación de estadísticas para esta sentencia |
Momento en el que comenzó la recopilación de estadísticas de mínimos/máximos para esta
sentencia (campos |
Por razones de seguridad, solo a los superusuarios y a los roles con privilegios del
rol pg_read_all_stats se les permite ver el texto SQL y el
queryid de las consultas ejecutadas por otros usuarios.
Sin embargo, otros usuarios pueden ver las estadísticas si la vista ha sido instalada
en su base de datos.
Las consultas planificables (es decir, SELECT, INSERT,
UPDATE, DELETE y MERGE)
y los comandos de utilidad se combinan en una única entrada de
pg_stat_statements siempre que tengan estructuras de consulta
idénticas según un cálculo hash interno. Típicamente, dos consultas se considerarán la misma
para este propósito si son semánticamente equivalentes excepto por los valores de las constantes literales
que aparecen en la consulta.
Los siguientes detalles sobre el reemplazo de constantes y
queryid solo se aplican cuando compute_query_id está habilitado. Si en su lugar utilizas un
módulo externo para calcular el queryid, deberías
consultar su documentación para obtener más detalles.
Cuando el valor de una constante se ha ignorado a efectos de hacer coincidir la consulta
con otras consultas, la constante se reemplaza por un símbolo de parámetro, como
$1, en la presentación de pg_stat_statements.
El resto del texto de la consulta es el de la primera consulta que tuvo el
valor hash queryid particular asociado con la
entrada de pg_stat_statements.
Las consultas sobre las cuales se puede aplicar normalización pueden observarse con valores constantes
en pg_stat_statements, especialmente cuando hay una alta tasa de desasignaciones
de entradas (deallocations). Para reducir la probabilidad de que esto ocurra, considera aumentar
pg_stat_statements.max. La vista pg_stat_statements_info,
que se detalla más adelante en Section F.32.2,
proporciona estadísticas sobre las desasignaciones de entradas.
En algunos casos, consultas con textos visiblemente diferentes pueden llegar a fusionarse en una
única entrada de pg_stat_statements; como se explicó anteriormente,
se espera que esto suceda para consultas semánticamente equivalentes.
Además, si la única diferencia entre las consultas es el número de elementos
en una lista de constantes, la lista se reducirá a un solo elemento pero se mostrará
con un indicador de lista comentado:
=# SELECT pg_stat_statements_reset(); =# SELECT * FROM test WHERE a IN (1, 2, 3, 4, 5, 6, 7); =# SELECT * FROM test WHERE a IN (1, 2, 3, 4, 5, 6, 7, 8); =# SELECT query, calls FROM pg_stat_statements WHERE query LIKE 'SELECT%'; -[ RECORD 1 ]------------------------------ query | SELECT * FROM test WHERE a IN ($1 /*, ... */) calls | 2
Además de estos casos, existe una pequeña probabilidad de que las colisiones de hash provoquen que consultas no relacionadas se fusionen en una sola entrada. (Sin embargo, esto no puede suceder para consultas pertenecientes a diferentes usuarios o bases de datos).
Dado que el valor hash queryid se calcula sobre la
representación de las consultas posterior al análisis sintáctico (post-parse-analysis representation), también es posible lo
contrario: consultas con textos idénticos pueden aparecer como
entradas separadas si tienen significados diferentes como resultado de
factores como configuraciones distintas de search_path.
Los consumidores de pg_stat_statements pueden querer usar el
queryid (tal vez en combinación con
dbid y userid) como un identificador más estable
y confiable para cada entrada que su texto de consulta.
Sin embargo, es importante entender que solo existen garantías limitadas sobre la
estabilidad del valor hash del queryid. Dado que el identificador
se deriva del árbol posterior al análisis sintáctico, su valor es una función de, entre otras
cosas, los identificadores internos de objetos que aparecen en esta representación.
Esto tiene algunas implicaciones contraintuitivas. Por ejemplo,
pg_stat_statements considerará que dos consultas aparentemente idénticas
son distintas si hacen referencia, por ejemplo, a una función que fue eliminada y
recreada entre las ejecuciones de las dos consultas.
Por el contrario, si una tabla se elimina y se vuelve a crear entre las ejecuciones de las
consultas, dos consultas aparentemente idénticas pueden considerarse iguales. Sin embargo,
si el alias de una tabla es diferente para consultas que por lo demás son similares, estas consultas
se considerarán distintas.
El proceso de hashing también es sensible a las diferencias en la arquitectura de la máquina
y otras facetas de la plataforma.
Además, no es seguro asumir que queryid se mantendrá estable entre
versiones mayores de PostgreSQL.
Se puede esperar que dos servidores que participan en una replicación basada en la reproducción física de WAL tengan
valores de queryid idénticos para la misma consulta. Sin embargo, los esquemas de replicación
lógica no prometen mantener las réplicas idénticas en todos los detalles relevantes, por lo que
el queryid no será un identificador útil para acumular costos
a través de un conjunto de réplicas lógicas. En caso de duda, se recomienda realizar pruebas directas.
Consumidores de queryid generalmente pueden asumir que los valores
son estables entre versiones menores de PostgreSQL,
siempre que las instancias se ejecuten en la misma arquitectura de máquina y los detalles de
los metadatos del catálogo coincidan. La compatibilidad solo se romperá entre versiones menores
como último recurso.
Los símbolos de parámetros utilizados para reemplazar constantes en los textos de consulta
representativos comienzan a partir del siguiente número después del parámetro
$n más alto en el texto original de la consulta,
o $1 si no había ninguno. Vale la pena señalar que en algunos casos
puede haber símbolos de parámetros ocultos que afecten a esta numeración. Por ejemplo,
PL/pgSQL utiliza símbolos de parámetros ocultos para insertar
valores de variables locales de función en las consultas, de modo que una sentencia de
PL/pgSQL como SELECT i + 1 INTO j tendría un texto
representativo como SELECT i + $2.
Los textos representativos de las consultas se guardan en un archivo de disco externo y no consumen
memoria compartida. Por lo tanto, incluso textos de consultas muy largos pueden almacenarse con éxito.
Sin embargo, si se acumulan muchos textos de consultas largos, el archivo externo podría crecer
de forma desmesurada. Como método de recuperación si eso sucede, pg_stat_statements puede
optar por descartar los textos de las consultas, tras lo cual todas las entradas existentes en la vista
pg_stat_statements mostrarán campos query nulos,
aunque se conservan las estadísticas asociadas con cada queryid. Si esto sucede,
considera reducir pg_stat_statements.max para evitar que vuelva a ocurrir.
No siempre se espera que plans y calls coincidan
porque las estadísticas de planificación y ejecución se actualizan en sus respectivas fases finales,
y solo para operaciones exitosas. Por ejemplo, si una sentencia se planifica con éxito pero falla durante
la fase de ejecución, solo se actualizarán sus estadísticas de planificación. Si se omite la planificación
porque se utiliza un plan almacenado en caché, solo se actualizarán sus estadísticas de ejecución.
pg_stat_statements_info #
Las estadísticas del propio módulo pg_stat_statements
se registran y están disponibles a través de una vista llamada
pg_stat_statements_info. Esta vista contiene
una única fila. Las columnas de la vista se muestran en la
Table F.23.
Table F.23. Columnas de pg_stat_statements_info
Columna Tipo Descripción |
|---|
Número total de veces que se desasignaron (deallocated) entradas de
|
Momento en el que se restablecieron por última vez todas las estadísticas en la vista
|
pg_stat_statements_reset(userid Oid, dbid Oid, queryid bigint, minmax_only boolean) returns timestamp with time zone
pg_stat_statements_reset descarta las estadísticas recopiladas
hasta el momento por pg_stat_statements que correspondan a los valores
especificados de userid, dbid y
queryid. Si alguno de los parámetros no se especifica, se utiliza el valor por
defecto 0 (inválido) para cada uno de ellos y se restablecerán las estadísticas
que coincidan con los demás parámetros. Si no se especifica ningún parámetro o si todos los parámetros especificados
son 0 (inválidos), se descartarán todas las estadísticas.
Si se descartan todas las estadísticas en la vista pg_stat_statements,
también se restablecerán las estadísticas en la vista pg_stat_statements_info.
Cuando minmax_only es true, solo se restablecerán los valores
de los tiempos mínimos y máximos de planificación y ejecución (es decir, los campos
min_plan_time, max_plan_time,
min_exec_time y max_exec_time). El valor por defecto para
el parámetro minmax_only es false. El momento del último restablecimiento
de mínimos/máximos realizado se muestra en el campo minmax_stats_since de la vista
pg_stat_statements.
Esta función devuelve la hora del restablecimiento. Esta hora se guarda en el campo
stats_reset de la vista pg_stat_statements_info o en el
campo minmax_stats_since de la vista pg_stat_statements
si el restablecimiento correspondiente se llevó a cabo realmente.
Por defecto, esta función solo puede ser ejecutada por superusuarios. El acceso puede otorgarse a otros
mediante el uso de GRANT.
pg_stat_statements(showtext boolean) returns setof record
La vista pg_stat_statements se define en términos de una función
también llamada pg_stat_statements. Es posible que los clientes llamen a la
función pg_stat_statements directamente y, especificando
showtext := false, se omita el texto de la consulta (es decir, el argumento
OUT que corresponde a la columna query de la vista devolverá
nulos). Esta característica está pensada para dar soporte a herramientas externas que puedan querer evitar la
sobrecarga de recuperar repetidamente textos de consultas de longitud indeterminada. En su lugar, estas herramientas
pueden almacenar en caché ellas mismas el primer texto de consulta observado para cada entrada, ya que eso es todo lo
que hace el propio pg_stat_statements, y luego recuperar los textos de las consultas solo
cuando sea necesario. Dado que el servidor almacena los textos de las consultas en un archivo, este enfoque puede
reducir las E/S físicas para el examen repetido de los datos de pg_stat_statements.
pg_stat_statements.max (integer)
pg_stat_statements.max es el número máximo de sentencias registradas por el módulo
(es decir, el número máximo de filas en la vista pg_stat_statements). Si se
observan más sentencias distintas que ese número, se descarta la información sobre las sentencias menos ejecutadas.
El número de veces que se descartó dicha información se puede ver en la vista
pg_stat_statements_info. El valor por defecto es 5000. Este parámetro solo se puede
establecer al iniciar el servidor.
pg_stat_statements.track (enum)
pg_stat_statements.track controla qué sentencias son contabilizadas por el módulo.
Especifica top para realizar el seguimiento de las sentencias de nivel superior (aquellas
emitidas directamente por los clientes), all para realizar también el seguimiento de las
sentencias anidadas (como las sentencias invocadas dentro de funciones) o none para desactivar
la recopilación de estadísticas de sentencias. El valor por defecto es top. Solo los
superusuarios pueden cambiar esta configuración.
pg_stat_statements.track_utility (boolean)
pg_stat_statements.track_utility controla si el módulo realiza el seguimiento de los comandos de
utilidad. Los comandos de utilidad son todos aquellos distintos de SELECT,
INSERT, UPDATE, DELETE y MERGE.
El valor por defecto es on. Solo los superusuarios pueden cambiar esta configuración.
pg_stat_statements.track_planning (boolean)
pg_stat_statements.track_planning controla si el módulo realiza el seguimiento de las operaciones
de planificación y su duración. La habilitación de este parámetro puede implicar una penalización de rendimiento
perceptible, especialmente cuando sentencias con estructura de consulta idéntica son ejecutadas por muchas conexiones
concurrentes que compiten por actualizar un número pequeño de entradas de pg_stat_statements.
El valor por defecto es off. Solo los superusuarios pueden cambiar esta configuración.
pg_stat_statements.save (boolean)
pg_stat_statements.save especifica si se deben guardar las estadísticas de las sentencias
a través de los reinicios y apagados del servidor. Si está en off, las estadísticas no se guardan
al apagar el servidor ni se vuelven a cargar al iniciarlo. El valor por defecto es on. Este
parámetro solo se puede establecer en el archivo postgresql.conf o en la línea de comandos del servidor.
El módulo requiere memoria compartida adicional proporcional a pg_stat_statements.max. Ten en cuenta
que esta memoria se consume siempre que el módulo se carga, incluso si pg_stat_statements.track
se establece en none.
Estos parámetros deben establecerse en el archivo postgresql.conf.
El uso típico podría ser:
# postgresql.conf shared_preload_libraries = 'pg_stat_statements' compute_query_id = on pg_stat_statements.max = 10000 pg_stat_statements.track = all
bench=# SELECT pg_stat_statements_reset();
$ pgbench -i bench
$ pgbench -c10 -t300 bench
bench=# \x
bench=# SELECT query, calls, total_exec_time, rows, 100.0 * shared_blks_hit /
nullif(shared_blks_hit + shared_blks_read, 0) AS hit_percent
FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 5;
-[ RECORD 1 ]---+--------------------------------------------------------------------
query | UPDATE pgbench_branches SET bbalance = bbalance + $1 WHERE bid = $2
calls | 3000
total_exec_time | 25565.855387
rows | 3000
hit_percent | 100.0000000000000000
-[ RECORD 2 ]---+--------------------------------------------------------------------
query | UPDATE pgbench_tellers SET tbalance = tbalance + $1 WHERE tid = $2
calls | 3000
total_exec_time | 20756.669379
rows | 3000
hit_percent | 100.0000000000000000
-[ RECORD 3 ]---+--------------------------------------------------------------------
query | copy pgbench_accounts from stdin
calls | 1
total_exec_time | 291.865911
rows | 100000
hit_percent | 100.0000000000000000
-[ RECORD 4 ]---+--------------------------------------------------------------------
query | UPDATE pgbench_accounts SET abalance = abalance + $1 WHERE aid = $2
calls | 3000
total_exec_time | 271.232977
rows | 3000
hit_percent | 98.8454011741682975
-[ RECORD 5 ]---+--------------------------------------------------------------------
query | alter table pgbench_accounts add primary key (aid)
calls | 1
total_exec_time | 160.588563
rows | 0
hit_percent | 100.0000000000000000
bench=# SELECT pg_stat_statements_reset(0,0,s.queryid) FROM pg_stat_statements AS s
WHERE s.query = 'UPDATE pgbench_branches SET bbalance = bbalance + $1 WHERE bid = $2';
bench=# SELECT query, calls, total_exec_time, rows, 100.0 * shared_blks_hit /
nullif(shared_blks_hit + shared_blks_read, 0) AS hit_percent
FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 5;
-[ RECORD 1 ]---+--------------------------------------------------------------------
query | UPDATE pgbench_tellers SET tbalance = tbalance + $1 WHERE tid = $2
calls | 3000
total_exec_time | 20756.669379
rows | 3000
hit_percent | 100.0000000000000000
-[ RECORD 2 ]---+--------------------------------------------------------------------
query | copy pgbench_accounts from stdin
calls | 1
total_exec_time | 291.865911
rows | 100000
hit_percent | 100.0000000000000000
-[ RECORD 3 ]---+--------------------------------------------------------------------
query | UPDATE pgbench_accounts SET abalance = abalance + $1 WHERE aid = $2
calls | 3000
total_exec_time | 271.232977
rows | 3000
hit_percent | 98.8454011741682975
-[ RECORD 4 ]---+--------------------------------------------------------------------
query | alter table pgbench_accounts add primary key (aid)
calls | 1
total_exec_time | 160.588563
rows | 0
hit_percent | 100.0000000000000000
-[ RECORD 5 ]---+--------------------------------------------------------------------
query | vacuum analyze pgbench_accounts
calls | 1
total_exec_time | 136.448116
rows | 0
hit_percent | 99.9201915403032721
bench=# SELECT pg_stat_statements_reset(0,0,0);
bench=# SELECT query, calls, total_exec_time, rows, 100.0 * shared_blks_hit /
nullif(shared_blks_hit + shared_blks_read, 0) AS hit_percent
FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 5;
-[ RECORD 1 ]---+-----------------------------------------------------------------------------
query | SELECT pg_stat_statements_reset(0,0,0)
calls | 1
total_exec_time | 0.189497
rows | 1
hit_percent |
-[ RECORD 2 ]---+-----------------------------------------------------------------------------
query | SELECT query, calls, total_exec_time, rows, $1 * shared_blks_hit / +
| nullif(shared_blks_hit + shared_blks_read, $2) AS hit_percent+
| FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT $3
calls | 0
total_exec_time | 0
rows | 0
hit_percent |
Takahiro Itagaki <[email protected]>.
Normalización de consultas añadida por Peter Geoghegan <[email protected]>.