Enhance PostgreSQL diagnostics script with improved connection pool status and query formatting
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
-- PostgreSQL Performance Diagnostics for Jellyfin
|
||||
-- Run this to diagnose current performance issues
|
||||
\pset pager off
|
||||
|
||||
\timing on
|
||||
\x auto
|
||||
@@ -16,14 +17,14 @@
|
||||
\echo '1. CONNECTION POOL STATUS'
|
||||
\echo '-----------------------------------------'
|
||||
|
||||
SELECT
|
||||
SELECT
|
||||
count(*) as total_connections,
|
||||
count(*) FILTER (WHERE state = 'active') as active,
|
||||
count(*) FILTER (WHERE state = 'idle') as idle,
|
||||
count(*) FILTER (WHERE state = 'idle in transaction') as idle_in_transaction,
|
||||
count(*) FILTER (WHERE state = 'idle in transaction (aborted)') as aborted,
|
||||
count(*) FILTER (WHERE wait_event_type IS NOT NULL) as waiting
|
||||
FROM pg_stat_activity
|
||||
FROM pg_stat_activity
|
||||
WHERE datname = 'jellyfin';
|
||||
|
||||
\echo ''
|
||||
@@ -35,7 +36,7 @@ WHERE datname = 'jellyfin';
|
||||
\echo '2. LONG-RUNNING QUERIES (>1 second)'
|
||||
\echo '-----------------------------------------'
|
||||
|
||||
SELECT
|
||||
SELECT
|
||||
pid,
|
||||
usename,
|
||||
application_name,
|
||||
@@ -45,8 +46,8 @@ SELECT
|
||||
wait_event,
|
||||
left(query, 100) as query_preview
|
||||
FROM pg_stat_activity
|
||||
WHERE datname = 'jellyfin'
|
||||
AND state != 'idle'
|
||||
WHERE datname = 'jellyfin'
|
||||
AND state != 'idle'
|
||||
AND query_start < now() - interval '1 second'
|
||||
ORDER BY duration DESC
|
||||
LIMIT 10;
|
||||
@@ -60,16 +61,21 @@ LIMIT 10;
|
||||
\echo '3. TABLE SIZES (Top 10)'
|
||||
\echo '-----------------------------------------'
|
||||
|
||||
SELECT
|
||||
SELECT
|
||||
s.schemaname,
|
||||
s.relname as tablename,
|
||||
pg_size_pretty(pg_total_relation_size(s.schemaname||'.'||s.relname)) AS total_size,
|
||||
pg_size_pretty(pg_relation_size(s.schemaname||'.'||s.relname)) AS table_size,
|
||||
pg_size_pretty(pg_total_relation_size(s.schemaname||'.'||s.relname) - pg_relation_size(s.schemaname||'.'||s.relname)) AS index_size,
|
||||
s.n_live_tup as row_count
|
||||
FROM pg_stat_user_tables s
|
||||
s.relname AS tablename,
|
||||
pg_size_pretty(pg_total_relation_size(fqname)) AS total_size,
|
||||
pg_size_pretty(pg_relation_size(fqname)) AS table_size,
|
||||
pg_size_pretty(pg_total_relation_size(fqname) - pg_relation_size(fqname)) AS index_size,
|
||||
s.n_live_tup AS row_count
|
||||
FROM (
|
||||
SELECT
|
||||
s.*,
|
||||
quote_ident(s.schemaname) || '.' || quote_ident(s.relname) AS fqname
|
||||
FROM pg_stat_user_tables s
|
||||
) s
|
||||
WHERE s.schemaname IN ('library', 'users', 'authentication', 'displaypreferences', 'activitylog')
|
||||
ORDER BY pg_total_relation_size(s.schemaname||'.'||s.relname) DESC
|
||||
ORDER BY pg_total_relation_size(fqname) DESC
|
||||
LIMIT 10;
|
||||
|
||||
\echo ''
|
||||
@@ -81,25 +87,25 @@ LIMIT 10;
|
||||
\echo '4. INDEX USAGE (Tables with low index usage)'
|
||||
\echo '-----------------------------------------'
|
||||
|
||||
SELECT
|
||||
SELECT
|
||||
schemaname,
|
||||
relname as tablename,
|
||||
seq_scan,
|
||||
idx_scan,
|
||||
n_live_tup as rows,
|
||||
CASE
|
||||
WHEN seq_scan + idx_scan > 0
|
||||
CASE
|
||||
WHEN seq_scan + idx_scan > 0
|
||||
THEN round(idx_scan::numeric / (seq_scan + idx_scan) * 100, 2)
|
||||
ELSE 0
|
||||
ELSE 0
|
||||
END as index_usage_percent
|
||||
FROM pg_stat_user_tables
|
||||
WHERE schemaname IN ('library', 'users', 'authentication', 'displaypreferences', 'activitylog')
|
||||
AND n_live_tup > 100
|
||||
ORDER BY
|
||||
CASE
|
||||
WHEN seq_scan + idx_scan > 0
|
||||
ORDER BY
|
||||
CASE
|
||||
WHEN seq_scan + idx_scan > 0
|
||||
THEN idx_scan::numeric / (seq_scan + idx_scan)
|
||||
ELSE 0
|
||||
ELSE 0
|
||||
END ASC
|
||||
LIMIT 10;
|
||||
|
||||
@@ -112,17 +118,17 @@ LIMIT 10;
|
||||
\echo '5. TABLES WITH HIGH SEQUENTIAL SCANS'
|
||||
\echo '-----------------------------------------'
|
||||
|
||||
SELECT
|
||||
SELECT
|
||||
schemaname,
|
||||
relname as tablename,
|
||||
seq_scan,
|
||||
seq_tup_read,
|
||||
idx_scan,
|
||||
n_live_tup,
|
||||
CASE
|
||||
WHEN seq_scan > 0
|
||||
THEN seq_tup_read / seq_scan
|
||||
ELSE 0
|
||||
CASE
|
||||
WHEN seq_scan > 0
|
||||
THEN seq_tup_read / seq_scan
|
||||
ELSE 0
|
||||
END as avg_rows_per_seq_scan
|
||||
FROM pg_stat_user_tables
|
||||
WHERE schemaname IN ('library', 'users', 'authentication', 'displaypreferences', 'activitylog')
|
||||
@@ -140,15 +146,15 @@ LIMIT 10;
|
||||
\echo '6. TABLE BLOAT (Dead Tuples)'
|
||||
\echo '-----------------------------------------'
|
||||
|
||||
SELECT
|
||||
SELECT
|
||||
schemaname,
|
||||
relname as tablename,
|
||||
n_dead_tup as dead_tuples,
|
||||
n_live_tup as live_tuples,
|
||||
CASE
|
||||
WHEN n_live_tup > 0
|
||||
CASE
|
||||
WHEN n_live_tup > 0
|
||||
THEN round(n_dead_tup::numeric / n_live_tup * 100, 2)
|
||||
ELSE 0
|
||||
ELSE 0
|
||||
END as dead_tuple_percent,
|
||||
last_vacuum,
|
||||
last_autovacuum,
|
||||
@@ -169,7 +175,7 @@ LIMIT 10;
|
||||
\echo '7. CACHE HIT RATIO (Should be >95%)'
|
||||
\echo '-----------------------------------------'
|
||||
|
||||
SELECT
|
||||
SELECT
|
||||
'Buffer Cache Hit Ratio' as metric,
|
||||
round(
|
||||
sum(blks_hit) / nullif(sum(blks_hit) + sum(blks_read), 0) * 100,
|
||||
@@ -187,7 +193,7 @@ WHERE datname = 'jellyfin';
|
||||
\echo '8. UNUSED OR RARELY USED INDEXES'
|
||||
\echo '-----------------------------------------'
|
||||
|
||||
SELECT
|
||||
SELECT
|
||||
schemaname,
|
||||
relname as tablename,
|
||||
indexrelname as indexname,
|
||||
@@ -199,8 +205,8 @@ FROM pg_stat_user_indexes
|
||||
WHERE schemaname IN ('library', 'users', 'authentication', 'displaypreferences', 'activitylog')
|
||||
AND idx_scan < 50
|
||||
AND pg_relation_size(indexrelid) > 1000000
|
||||
ORDER BY pg_relation_size(indexrelid) DESC
|
||||
LIMIT 10;
|
||||
ORDER BY tablename, pg_relation_size(indexrelid) DESC;
|
||||
--LIMIT 10;
|
||||
|
||||
\echo ''
|
||||
|
||||
@@ -211,7 +217,7 @@ LIMIT 10;
|
||||
\echo '9. CURRENT LOCKS (Blocked queries)'
|
||||
\echo '-----------------------------------------'
|
||||
|
||||
SELECT
|
||||
SELECT
|
||||
bl.pid AS blocked_pid,
|
||||
a.usename AS blocked_user,
|
||||
ka.query AS blocking_query,
|
||||
@@ -250,8 +256,8 @@ DECLARE
|
||||
query_rec RECORD;
|
||||
BEGIN
|
||||
IF EXISTS (SELECT 1 FROM pg_extension WHERE extname = 'pg_stat_statements') THEN
|
||||
FOR query_rec IN
|
||||
SELECT
|
||||
FOR query_rec IN
|
||||
SELECT
|
||||
calls,
|
||||
round(mean_exec_time::numeric, 2) as avg_time_ms,
|
||||
round(max_exec_time::numeric, 2) as max_time_ms,
|
||||
@@ -264,12 +270,12 @@ BEGIN
|
||||
ORDER BY mean_exec_time DESC
|
||||
LIMIT 10
|
||||
LOOP
|
||||
RAISE NOTICE 'Calls: %, Avg: %ms, Max: %ms, Total: %ms, %%: %, Query: %',
|
||||
query_rec.calls,
|
||||
query_rec.avg_time_ms,
|
||||
query_rec.max_time_ms,
|
||||
query_rec.total_time_ms,
|
||||
query_rec.percent_total,
|
||||
RAISE NOTICE 'Calls: %, Avg: %ms, Max: %ms, Total: %ms, %%: %, Query: %',
|
||||
query_rec.calls,
|
||||
query_rec.avg_time_ms,
|
||||
query_rec.max_time_ms,
|
||||
query_rec.total_time_ms,
|
||||
query_rec.percent_total,
|
||||
query_rec.query_preview;
|
||||
END LOOP;
|
||||
END IF;
|
||||
@@ -284,7 +290,7 @@ END $$;
|
||||
\echo '11. KEY POSTGRESQL SETTINGS'
|
||||
\echo '-----------------------------------------'
|
||||
|
||||
SELECT
|
||||
SELECT
|
||||
name,
|
||||
setting,
|
||||
unit,
|
||||
@@ -324,42 +330,42 @@ BEGIN
|
||||
-- Check connection count
|
||||
SELECT count(*) INTO conn_count
|
||||
FROM pg_stat_activity WHERE datname = 'jellyfin';
|
||||
|
||||
|
||||
SELECT setting::int INTO max_conn
|
||||
FROM pg_settings WHERE name = 'max_connections';
|
||||
|
||||
|
||||
IF conn_count::numeric / max_conn > 0.8 THEN
|
||||
RAISE WARNING 'High connection usage: % of % max connections', conn_count, max_conn;
|
||||
RAISE NOTICE 'RECOMMENDATION: Increase max_connections in postgresql.conf';
|
||||
END IF;
|
||||
|
||||
|
||||
-- Check cache hit ratio
|
||||
SELECT round(
|
||||
sum(blks_hit) / nullif(sum(blks_hit) + sum(blks_read), 0) * 100, 2
|
||||
) INTO hit_ratio
|
||||
FROM pg_stat_database WHERE datname = 'jellyfin';
|
||||
|
||||
|
||||
IF hit_ratio < 95 THEN
|
||||
RAISE WARNING 'Low cache hit ratio: %%. Target: >95%%', hit_ratio;
|
||||
RAISE WARNING 'Low cache hit ratio: %. Target: >95%%', hit_ratio;
|
||||
RAISE NOTICE 'RECOMMENDATION: Increase shared_buffers and effective_cache_size';
|
||||
END IF;
|
||||
|
||||
|
||||
-- Check bloat
|
||||
SELECT count(*) INTO bloat_tables
|
||||
FROM pg_stat_user_tables
|
||||
WHERE n_dead_tup > 1000
|
||||
AND schemaname IN ('library', 'users', 'authentication');
|
||||
|
||||
|
||||
IF bloat_tables > 0 THEN
|
||||
RAISE WARNING 'Found % tables with significant dead tuples', bloat_tables;
|
||||
RAISE NOTICE 'RECOMMENDATION: Run VACUUM ANALYZE or adjust autovacuum settings';
|
||||
END IF;
|
||||
|
||||
RAISE NOTICE 'Diagnostic report complete.';
|
||||
|
||||
-- RAISE NOTICE 'Diagnostic report complete.';
|
||||
END $$;
|
||||
|
||||
\echo ''
|
||||
\echo '========================================='
|
||||
\echo 'Run sql/performance_indexes.sql to add missing indexes'
|
||||
\echo 'See PERFORMANCE_TROUBLESHOOTING.md for detailed fixes'
|
||||
\echo '========================================='
|
||||
\echo '========================================='
|
||||
Reference in New Issue
Block a user