Skip to content
Back to skills

Pg Stat Statements Observability

ASecurity

pg_stat_statements Query Observability

  • 17 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 2, 2026
ai-agentsgosqlazuredatabasesecurityperformance

Security analysis

A100/100

Scanned September 2, 2026

npx -y skills add CarlosCaPe/octorato --skill pg-stat-statements-observability --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Pg Stat Statements Observability?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Pg Stat Statements Observability
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/carloscape-pg-stat-statements-observability/badge)](https://www.skillsdirectory.com/skills/carloscape-pg-stat-statements-observability)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: pg-stat-statements-observability
description: "pg_stat_statements Query Observability"
metadata:
  short-description: "pg_stat_statements Query Observability"
  original-index: 33
---

# pg_stat_statements Query Observability

> Source: [PostgreSQL Best Practices](../DOCUMENTS/PostgreSQL_BestPractices_Azure.md)
> -- "Server configuration", 

## What

Enabling and using the `pg_stat_statements` extension to capture per-query
performance metrics (calls, total time, mean time, rows, shared blocks).
This is the primary tool for identifying slow queries, regression, and
optimization opportunities.

## Why

Without query-level metrics:
- Slow queries hide behind aggregate server metrics (CPU, IOPS)
- Index optimization is guesswork -- you cannot tell which queries dominate
- Performance regressions after deployments go undetected
- Capacity planning has no data foundation

`pg_stat_statements` is lightweight (< 1% overhead), built into PostgreSQL,
and available on Azure Flexible Server as a loadable module.

## How

### Enable the extension (Azure Flexible Server)

1. Azure Portal > Server Parameters:
   ```text
   shared_preload_libraries = pg_stat_statements   -- requires restart
   pg_stat_statements.track = all                  -- track all statements
   pg_stat_statements.max = 5000                   -- max tracked queries
   ```
2. Restart the server (required for `shared_preload_libraries`).
3. Create the extension in each database:
   ```sql
   CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
   ```

### Top 10 queries by total execution time

```sql
SELECT
    calls,
    ROUND(total_exec_time::numeric, 2)       AS total_ms,
    ROUND(mean_exec_time::numeric, 2)        AS mean_ms,
    ROUND(min_exec_time::numeric, 2)         AS min_ms,
    ROUND(max_exec_time::numeric, 2)         AS max_ms,
    rows,
    LEFT(query, 120)                         AS query_preview
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 10;
```

### Top 10 queries by mean execution time (slow outliers)

```sql
SELECT
    calls,
    ROUND(mean_exec_time::numeric, 2)        AS mean_ms,
    ROUND(total_exec_time::numeric, 2)       AS total_ms,
    rows,
    LEFT(query, 120)                         AS query_preview
FROM pg_stat_statements
WHERE calls > 10                             -- ignore one-off admin queries
ORDER BY mean_exec_time DESC
LIMIT 10;
```

### Top 10 queries by shared buffer reads (I/O heavy)

```sql
SELECT
    calls,
    shared_blks_read + shared_blks_hit       AS total_blks,
    ROUND(
        100.0 * shared_blks_hit /
        NULLIF(shared_blks_read + shared_blks_hit, 0), 2
    )                                        AS cache_hit_pct,
    ROUND(mean_exec_time::numeric, 2)        AS mean_ms,
    LEFT(query, 120)                         AS query_preview
FROM pg_stat_statements
ORDER BY shared_blks_read DESC
LIMIT 10;
```

### Reset statistics (after optimization to re-baseline)

```sql
-- Reset ALL query stats (use sparingly)
SELECT pg_stat_statements_reset();
```

### Check if extension is active

```sql
SELECT * FROM pg_available_extensions
WHERE name = 'pg_stat_statements';

-- If installed, verify it is tracking
SELECT COUNT(*) AS tracked_queries
FROM pg_stat_statements;
```

### Combine with slow query logging

```text
-- Server parameter (Azure Portal)
log_min_duration_statement = 250    -- log queries > 250ms

-- This gives you two layers:
-- 1. pg_stat_statements: aggregated stats for ALL queries
-- 2. PostgreSQL log: individual slow query events with parameters
```

## Key Metrics to Monitor

| Metric | What It Tells You | Action Threshold |
|--------|------------------|-----------------|
| `total_exec_time` | Cumulative CPU cost | Top query > 50% of total |
| `mean_exec_time` | Per-call latency | > 100ms for OLTP queries |
| `calls` | Query frequency | Top query > 10K calls/hour |
| `rows` | Rows returned per call | > 10K rows (missing pagination?) |
| `shared_blks_read` | Disk I/O | High reads = missing index or seq scan |
| `cache_hit_pct` | Buffer cache effectiveness | < 95% = under-provisioned memory |

## When to Use

- After enabling on a new server  pattern)
- Before and after index changes to measure impact
- During audit investigations to find optimization targets
- Periodically (weekly/monthly) for capacity planning

## Where We Used It

- ****: Enabled `pg_stat_statements` on DEV and QA servers
- **Best Practices**: Listed as mandatory server configuration
- **Audit TDDs**: Query-level observability requirement

## Related Skills

- **Skill #29** (EXPLAIN ANALYZE) -- detailed plan for individual queries
- **Skill #30** (Autovacuum & Bloat) -- bloat causes seq scans, visible here
- **Skill #32** (Connection Pooling) -- slow queries identified here, timeouts set there

## References

- [PostgreSQL Best Practices](../DOCUMENTS/PostgreSQL_BestPractices_Azure.md)
  -- "Server configuration" section
- [User Management Audit TDD](../DOCUMENTS/user_mgmt_DB_Audit_TDD.md)
  -- "Query-level observability" requirement

## Gotchas

- `shared_preload_libraries` change requires a **server restart** on Azure
  Flexible Server -- coordinate with ops
- Statistics persist across connections but are lost on server restart
  unless `pg_stat_statements.save = on` (default on Azure)
- `pg_stat_statements_reset()` clears ALL stats -- there is no per-query
  reset; use it only after a known baseline change
- The `query` column normalizes literal values to `$1, $2, ...` -- you
  cannot see actual parameter values (by design, for security)
- `pg_stat_statements.max = 5000` means the 5001st unique query evicts
  the least-used entry -- increase if your app has many dynamic queries
- On Azure, you may need the `azure_pg_admin` role to create the extension

---

*Category: Tooling | Origin: *

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.