Repository navigation
[POSTGRESQL] PostgreSQL Production Monitoring Queries
Kumpulan query untuk monitoring PostgreSQL 15 production.
| Parameter | Value |
|---|---|
| PostgreSQL | 15 |
| RAM | 16 GB |
| Current max_connections | 100 |
| Target max_connections | 200 |
| Swap | 0 |
| Config | /etc/postgresql/15/main/postgresql.conf |
SELECT
state,
count(*) AS connections
FROM pg_stat_activity
GROUP BY state
ORDER BY connections DESC;Status yang perlu diperhatikan:
-
active— sedang menjalankan query -
idle— connection tersedia tetapi sedang tidak menjalankan query -
idle in transaction— transaction terbuka tetapi tidak sedang menjalankan query -
NULL— biasanya proses background PostgreSQL
SELECT
current_setting('max_connections')::int AS max_connections,
count(*) AS current_connections,
round(
100.0 * count(*) /
current_setting('max_connections')::int,
2
) AS usage_percent
FROM pg_stat_activity;| Usage | Status |
|---|---|
| < 60% | Normal |
| 60–80% | Monitor |
| 80–90% | Warning |
| 90–95% | Critical |
| > 95% | Emergency |
Dengan max_connections = 200:
- 80% = 160 connections
- 90% = 180 connections
- 95% = 190 connections
SELECT
usename,
count(*) AS connections
FROM pg_stat_activity
GROUP BY usename
ORDER BY connections DESC;Berguna untuk mengetahui PostgreSQL user mana yang paling banyak menggunakan connection.
SELECT
datname,
count(*) AS connections
FROM pg_stat_activity
GROUP BY datname
ORDER BY connections DESC;SELECT
application_name,
count(*) AS connections
FROM pg_stat_activity
GROUP BY application_name
ORDER BY connections DESC;SELECT
usename,
application_name,
client_addr,
state,
count(*) AS connections
FROM pg_stat_activity
GROUP BY
usename,
application_name,
client_addr,
state
ORDER BY connections DESC;Ini merupakan query utama untuk investigasi TooManyConnections.
SELECT
count(*) AS active_connections
FROM pg_stat_activity
WHERE state = 'active';SELECT
pid,
usename,
application_name,
client_addr,
now() - query_start AS duration,
state,
wait_event_type,
wait_event,
LEFT(query, 300) AS query
FROM pg_stat_activity
WHERE state = 'active'
ORDER BY query_start;Menampilkan query yang berjalan lebih dari 5 detik.
SELECT
pid,
usename,
application_name,
client_addr,
now() - query_start AS duration,
wait_event_type,
wait_event,
LEFT(query, 500) AS query
FROM pg_stat_activity
WHERE state = 'active'
AND now() - query_start > interval '5 seconds'
ORDER BY query_start;Recommended thresholds:
-
5 seconds → investigate
-
30 seconds → warning
-
60 seconds → critical
SELECT
pid,
usename,
application_name,
client_addr,
now() - xact_start AS transaction_age,
now() - state_change AS idle_duration,
LEFT(query, 300) AS query
FROM pg_stat_activity
WHERE state = 'idle in transaction'
ORDER BY xact_start;Jika terdapat idle in transaction selama beberapa menit atau lebih, lakukan investigasi.
Potensi dampak:
- lock
- bloat
- VACUUM tertahan
- penggunaan resource meningkat
SELECT
pid,
usename,
application_name,
client_addr,
backend_start,
state_change,
now() - state_change AS idle_for
FROM pg_stat_activity
WHERE state = 'idle'
ORDER BY state_change ASC;SELECT
pid,
usename,
application_name,
client_addr,
state,
now() - backend_start AS connection_age
FROM pg_stat_activity
ORDER BY backend_start;SELECT
blocked.pid AS blocked_pid,
blocked.usename AS blocked_user,
blocking.pid AS blocking_pid,
blocking.usename AS blocking_user,
now() - blocked.query_start AS blocked_duration,
LEFT(blocked.query, 200) AS blocked_query,
LEFT(blocking.query, 200) AS blocking_query
FROM pg_stat_activity blocked
JOIN pg_stat_activity blocking
ON blocking.pid = ANY(pg_blocking_pids(blocked.pid))
ORDER BY blocked.query_start;Jika hasilnya 0 rows, tidak ada blocking pada saat query dijalankan.
SELECT
datname,
pg_size_pretty(pg_database_size(datname)) AS size
FROM pg_database
ORDER BY pg_database_size(datname) DESC;Jalankan pada database yang ingin diperiksa.
SELECT
schemaname,
relname AS table_name,
pg_size_pretty(pg_total_relation_size(relid)) AS total_size,
pg_size_pretty(pg_relation_size(relid)) AS table_size,
pg_size_pretty(pg_indexes_size(relid)) AS index_size
FROM pg_catalog.pg_statio_user_tables
ORDER BY pg_total_relation_size(relid) DESC
LIMIT 20;-
total_size— table + index -
table_size— ukuran data table -
index_size— ukuran index
SELECT
sum(blks_hit) AS cache_hits,
sum(blks_read) AS disk_reads,
round(
100.0 * sum(blks_hit) /
NULLIF(sum(blks_hit) + sum(blks_read), 0),
2
) AS cache_hit_ratio
FROM pg_stat_database;Untuk workload OLTP, cache hit ratio yang tinggi umumnya diharapkan.
SELECT
datname,
xact_commit,
xact_rollback,
tup_inserted,
tup_updated,
tup_deleted,
tup_returned,
tup_fetched
FROM pg_stat_database
WHERE datname IS NOT NULL
ORDER BY xact_commit DESC;SELECT
datname,
deadlocks,
conflicts,
temp_files,
temp_bytes
FROM pg_stat_database
WHERE datname IS NOT NULL
ORDER BY deadlocks DESC;Perhatikan terutama jika nilai deadlocks terus bertambah.
SHOW max_connections;SELECT
name,
setting,
unit
FROM pg_settings
WHERE name IN (
'max_connections',
'shared_buffers',
'work_mem',
'maintenance_work_mem'
);SHOW config_file;Contoh:
/etc/postgresql/15/main/postgresql.conf
SELECT
current_setting('max_connections')::int AS max_connections,
count(*) AS current_connections,
round(
100.0 * count(*) /
current_setting('max_connections')::int,
2
) AS usage_percent
FROM pg_stat_activity;SELECT
state,
count(*) AS connections
FROM pg_stat_activity
GROUP BY state
ORDER BY connections DESC;SELECT
usename,
count(*) AS connections
FROM pg_stat_activity
GROUP BY usename
ORDER BY connections DESC;SELECT
pid,
usename,
application_name,
now() - query_start AS duration,
wait_event_type,
wait_event,
LEFT(query, 300) AS query
FROM pg_stat_activity
WHERE state = 'active'
ORDER BY query_start;SELECT
blocked.pid AS blocked_pid,
blocking.pid AS blocking_pid,
now() - blocked.query_start AS blocked_duration,
LEFT(blocked.query, 200) AS blocked_query,
LEFT(blocking.query, 200) AS blocking_query
FROM pg_stat_activity blocked
JOIN pg_stat_activity blocking
ON blocking.pid = ANY(pg_blocking_pids(blocked.pid))
ORDER BY blocked.query_start;SELECT
pid,
usename,
application_name,
client_addr,
now() - xact_start AS transaction_age,
LEFT(query, 300) AS query
FROM pg_stat_activity
WHERE state = 'idle in transaction'
ORDER BY xact_start;Server reference:
RAM : 16 GB
PostgreSQL : 15
max_connections : 200
Swap : 0
| Priority | Metric | Warning |
|---|---|---|
| High | Connection usage | > 80% |
| High | Idle in transaction | > 1–5 min |
| High | Blocking query | Any |
| Medium | Long query | > 5 sec |
| Medium | Deadlock | Increasing |
| Medium | Cache hit ratio | Decreasing |
| Medium | Database size | Rapid growth |
| Medium | Table/index size | Rapid growth |
Dengan:
max_connections = 200
gunakan prinsip berikut:
| Connections | Status |
|---|---|
| 0–120 | Normal |
| 120–160 | Monitor |
| 160–180 | Warning |
| 180–190 | Critical |
| 190–200 | Emergency |
Jangan menargetkan penggunaan hingga 200.
Sediakan connection reserve untuk:
- PostgreSQL administration
- monitoring
- maintenance
- unexpected traffic spike
- application reconnect
- troubleshooting
sudo systemctl status postgresql --no-pagersudo -u postgres psql -c "SELECT version();"sudo -u postgres psql -c "SHOW config_file;"sudo -u postgres psql -c "SHOW max_connections;"sudo -u postgres psql -c \
"SELECT count(*) AS connections FROM pg_stat_activity;"free -mnprocdf -hJika aplikasi mendapatkan:
P2037
TooManyConnections
remaining connection slots are reserved
jalankan:
SELECT
current_setting('max_connections')::int AS max_connections,
count(*) AS current_connections,
round(
100.0 * count(*) /
current_setting('max_connections')::int,
2
) AS usage_percent
FROM pg_stat_activity;Kemudian:
SELECT
usename,
application_name,
client_addr,
state,
count(*) AS connections
FROM pg_stat_activity
GROUP BY
usename,
application_name,
client_addr,
state
ORDER BY connections DESC;Kemudian:
SELECT
pid,
usename,
application_name,
client_addr,
state,
now() - state_change AS idle_for,
LEFT(query, 200) AS query
FROM pg_stat_activity
ORDER BY state_change;Jangan langsung melakukan pg_terminate_backend() terhadap semua connection. Identifikasi service terlebih dahulu.