pg_reports 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,310 @@
1
+ en:
2
+ pg_reports:
3
+ documentation:
4
+ # === QUERIES ===
5
+ slow_queries:
6
+ title: "Slow Queries"
7
+ what: "Queries with high mean execution time."
8
+ how: "Data is collected from pg_stat_statements — a PostgreSQL extension that tracks execution statistics for all SQL queries. Mean time is calculated as total_time / calls."
9
+ nuances:
10
+ - "PostgreSQL planner may NOT use indexes for small tables (< 10-50K rows) if sequential scan is faster — this is normal behavior."
11
+ - "High mean time can be caused by: missing index, suboptimal plan, locks, disk I/O load."
12
+ - "For composite indexes, column order matters — index (a, b) is effective for WHERE a = ? and WHERE a = ? AND b = ?, but not for WHERE b = ?."
13
+ - "Queries with LIKE '%pattern%' cannot use B-tree indexes — consider pg_trgm or full-text search."
14
+
15
+ heavy_queries:
16
+ title: "Heavy Queries"
17
+ what: "Most frequently called queries (by number of calls)."
18
+ how: "Counts the calls field from pg_stat_statements. High call count increases overall database load, even if each individual query is fast."
19
+ nuances:
20
+ - "Frequent queries are prime candidates for application-level caching (Redis, Memcached)."
21
+ - "N+1 problem: if you see many similar queries with different IDs — this is a sign of N+1, need eager loading."
22
+ - "Consider batch operations instead of multiple single queries."
23
+ - "Prepared statements can reduce parsing overhead for frequent queries."
24
+
25
+ expensive_queries:
26
+ title: "Expensive Queries"
27
+ what: "Queries consuming the most total time (total_time = mean_time × calls)."
28
+ how: "Sorting by total_time shows queries with the greatest impact on overall database performance."
29
+ nuances:
30
+ - "A query can be fast but called so often that it takes a lot of time in total."
31
+ - "Optimizing expensive queries has the greatest effect on overall system performance."
32
+ - "Consider: adding indexes, rewriting the query, caching results, materialized views."
33
+
34
+ missing_index_queries:
35
+ title: "Missing Index Queries"
36
+ what: "Queries performing sequential scans on large tables instead of using indexes."
37
+ how: "Analyzes seq_scan and seq_tup_read counts for tables affected by queries. High number of rows read during seq_scan indicates potential need for an index."
38
+ nuances:
39
+ - "Sequential scan is NORMAL for: small tables, queries returning >5-10% of data, tables without suitable columns for indexing."
40
+ - "Composite index (a, b, c) covers: WHERE a, WHERE a AND b, WHERE a AND b AND c, but does NOT cover WHERE b or WHERE c."
41
+ - "EXPLAIN ANALYZE will show the actual query execution plan."
42
+ - "Partial indexes (CREATE INDEX ... WHERE condition) save space and speed up specific queries."
43
+
44
+ low_cache_hit_queries:
45
+ title: "Low Cache Hit Queries"
46
+ what: "Queries with low cache hit ratio (shared_buffers)."
47
+ how: "Calculated as shared_blks_hit / (shared_blks_hit + shared_blks_read). Low cache hit ratio means frequent disk reads."
48
+ nuances:
49
+ - "Target cache hit ratio: >95% for active data."
50
+ - "Low cache hit may indicate: insufficient shared_buffers, tables too large, inefficient queries scanning too much data."
51
+ - "Increasing shared_buffers helps, but there's a limit to effectiveness (typically 25% of RAM)."
52
+ - "Covering indexes (INCLUDE) allow index-only scans without accessing the table."
53
+
54
+ all_queries:
55
+ title: "All Queries"
56
+ what: "Complete statistics for all queries from pg_stat_statements."
57
+ how: "Displays all tracked queries with their metrics: calls, total_time, mean_time, rows, etc."
58
+ nuances:
59
+ - "pg_stat_statements normalizes queries by replacing literals with $1, $2, etc."
60
+ - "Statistics are reset on: PostgreSQL restart, pg_stat_statements_reset() call, reaching track_activity_query_size limit."
61
+ - "The pg_stat_statements.max parameter controls the maximum number of tracked queries (default 5000)."
62
+
63
+ # === INDEXES ===
64
+ unused_indexes:
65
+ title: "Unused Indexes"
66
+ what: "Indexes that are rarely or never used for scanning."
67
+ how: "Analyzes idx_scan from pg_stat_user_indexes. Indexes with zero or very low scan counts are candidates for removal."
68
+ nuances:
69
+ - "Before removal, ensure statistics were collected over a sufficient period (including all workload types: reports, batch operations)."
70
+ - "Indexes are still updated on INSERT/UPDATE/DELETE, even if not used for reading — this is overhead."
71
+ - "Unique indexes (UNIQUE) may have 0 scans but are needed for constraints."
72
+ - "Foreign key indexes are important for DELETE/UPDATE performance on parent tables."
73
+
74
+ duplicate_indexes:
75
+ title: "Duplicate Indexes"
76
+ what: "Indexes that duplicate other indexes (one is a prefix of another)."
77
+ how: "Compares index definitions. Index (a) is redundant when (a, b) exists. Also detects full duplicates."
78
+ nuances:
79
+ - "Duplicates waste disk space and slow down INSERT/UPDATE/DELETE."
80
+ - "However, sometimes a 'duplicate' is needed: different index types (B-tree vs GIN), partial vs full index."
81
+ - "Index (a, b) does NOT fully replace (b, a) — column order is critical."
82
+
83
+ invalid_indexes:
84
+ title: "Invalid Indexes"
85
+ what: "Indexes that failed to build successfully (invalid state)."
86
+ how: "Checks the indisvalid flag in pg_index. False means the index won't be used by the planner."
87
+ nuances:
88
+ - "Usually appear after a failed CREATE INDEX CONCURRENTLY."
89
+ - "Solution: DROP INDEX and recreate."
90
+ - "REINDEX CONCURRENTLY can help for existing invalid indexes (PostgreSQL 12+)."
91
+
92
+ missing_indexes:
93
+ title: "Missing Indexes"
94
+ what: "Tables with high sequential scan counts that probably need indexes."
95
+ how: "Analyzes the ratio of seq_scan vs idx_scan and number of rows read (seq_tup_read)."
96
+ nuances:
97
+ - "Not all seq_scans are bad — for small tables it's optimal."
98
+ - "Analyze specific queries via pg_stat_statements to understand which columns to index."
99
+ - "Use EXPLAIN ANALYZE to verify if an index will be used."
100
+
101
+ index_usage:
102
+ title: "Index Usage"
103
+ what: "Index usage statistics: how many times scanned, how many rows returned."
104
+ how: "Data from pg_stat_user_indexes shows the effectiveness of each index."
105
+ nuances:
106
+ - "idx_scan — number of index scan operations."
107
+ - "idx_tup_read — rows read from the index."
108
+ - "idx_tup_fetch — rows fetched from table after index scan (for non-covering indexes)."
109
+
110
+ bloated_indexes:
111
+ title: "Bloated Indexes"
112
+ what: "Indexes with high bloat level due to dead tuples."
113
+ how: "Estimates the ratio of actual size to expected size. Bloat appears after UPDATE/DELETE operations."
114
+ nuances:
115
+ - "REINDEX or REINDEX CONCURRENTLY rebuilds the index without bloat."
116
+ - "Regular VACUUM keeps bloat at acceptable levels."
117
+ - "pg_repack allows rebuilding indexes without locks."
118
+ - "Bloat >30-50% — reason for action."
119
+
120
+ index_sizes:
121
+ title: "Index Sizes"
122
+ what: "Index sizes on disk."
123
+ how: "Shows pg_relation_size for each index."
124
+ nuances:
125
+ - "Large indexes require more memory for effective caching."
126
+ - "Partial indexes can significantly reduce size: CREATE INDEX ... WHERE active = true."
127
+ - "BRIN indexes are much more compact than B-tree for certain data patterns (e.g., timestamp columns)."
128
+
129
+ # === TABLES ===
130
+ table_sizes:
131
+ title: "Table Sizes"
132
+ what: "Table sizes on disk (data + TOAST + indexes)."
133
+ how: "Uses pg_total_relation_size for complete size including all related objects."
134
+ nuances:
135
+ - "TOAST stores large values (>2KB) in a separate table."
136
+ - "Partitioning helps manage large tables."
137
+ - "Regular VACUUM FULL can reduce size, but locks the table."
138
+
139
+ bloated_tables:
140
+ title: "Bloated Tables"
141
+ what: "Tables with high dead tuple percentage."
142
+ how: "Analyzes n_dead_tup from pg_stat_user_tables. Dead tuples appear after UPDATE/DELETE."
143
+ nuances:
144
+ - "VACUUM removes dead tuples and frees space for reuse."
145
+ - "VACUUM FULL physically reduces file size, but locks the table."
146
+ - "autovacuum should handle this automatically — check its settings if you see high bloat."
147
+ - "Dead tuple ratio >20% — signal of vacuum problems."
148
+
149
+ vacuum_needed:
150
+ title: "Vacuum Needed"
151
+ what: "Tables that need VACUUM based on dead tuple statistics and last vacuum time."
152
+ how: "Combines data about dead tuples, last vacuum, and table size."
153
+ nuances:
154
+ - "last_vacuum and last_autovacuum show when vacuum last ran."
155
+ - "If autovacuum can't keep up, you can: lower autovacuum_vacuum_threshold, increase autovacuum_vacuum_scale_factor, add workers."
156
+ - "For critical tables, you can set individual parameters: ALTER TABLE SET (autovacuum_vacuum_threshold = 1000)."
157
+
158
+ row_counts:
159
+ title: "Row Counts"
160
+ what: "Approximate row counts in tables."
161
+ how: "Uses n_live_tup from pg_stat_user_tables (updated by ANALYZE) or reltuples from pg_class."
162
+ nuances:
163
+ - "This is an approximate estimate, exact COUNT(*) may differ."
164
+ - "ANALYZE updates statistics, autovacuum runs it automatically."
165
+ - "For exact count use SELECT COUNT(*), but this can be slow on large tables."
166
+
167
+ cache_hit_ratios:
168
+ title: "Cache Hit Ratios"
169
+ what: "Cache hit ratio (shared buffers) for each table."
170
+ how: "Calculated as heap_blks_hit / (heap_blks_hit + heap_blks_read)."
171
+ nuances:
172
+ - "Target value: >95% for actively used tables."
173
+ - "Low cache hit: table too large for cache or rarely used."
174
+ - "Solutions: increase shared_buffers, optimize queries, add indexes to reduce scanned data."
175
+
176
+ seq_scans:
177
+ title: "Sequential Scans"
178
+ what: "Tables with high sequential scan operation count."
179
+ how: "Data from pg_stat_user_tables shows seq_scan vs idx_scan."
180
+ nuances:
181
+ - "seq_scan is not always bad — for small tables it's optimal."
182
+ - "seq_tup_read shows how many rows were read — this is more important than scan count."
183
+ - "High seq_tup_read/seq_scan = many rows per scan = probably normal."
184
+ - "Low seq_tup_read/seq_scan = many small scans = possibly N+1."
185
+
186
+ recently_modified:
187
+ title: "Recently Modified"
188
+ what: "Tables with recent INSERT/UPDATE/DELETE activity."
189
+ how: "Analyzes n_tup_ins, n_tup_upd, n_tup_del from pg_stat_user_tables."
190
+ nuances:
191
+ - "Helps understand write patterns to the database."
192
+ - "High UPDATE activity creates dead tuples — monitor vacuum."
193
+ - "HOT updates (Heap Only Tuple) are more efficient — indexes are not updated."
194
+
195
+ # === CONNECTIONS ===
196
+ active_connections:
197
+ title: "Active Connections"
198
+ what: "Current database connections."
199
+ how: "Data from pg_stat_activity shows all active backend processes."
200
+ nuances:
201
+ - "Each connection consumes memory (~10MB per connection)."
202
+ - "max_connections limits the total number — don't set it too high."
203
+ - "Use a connection pooler (PgBouncer, pgpool-II) for scaling."
204
+ - "state: active = executing query, idle = waiting, idle in transaction = dangerous."
205
+
206
+ connection_stats:
207
+ title: "Connection Stats"
208
+ what: "Connection statistics grouped by state."
209
+ how: "Aggregation of pg_stat_activity by state field."
210
+ nuances:
211
+ - "idle in transaction: transaction open but not active — blocks VACUUM, holds locks."
212
+ - "idle in transaction (aborted): error in transaction, waiting for ROLLBACK."
213
+ - "Configure idle_in_transaction_session_timeout for automatic termination."
214
+
215
+ long_running_queries:
216
+ title: "Long Running Queries"
217
+ what: "Queries running longer than the specified threshold."
218
+ how: "Filters pg_stat_activity by duration = now() - query_start."
219
+ nuances:
220
+ - "Long queries can hold locks and interfere with other processes."
221
+ - "statement_timeout can automatically terminate queries running too long."
222
+ - "For reports/analytics, use a read replica or configure a separate connection pool."
223
+
224
+ blocking_queries:
225
+ title: "Blocking Queries"
226
+ what: "Queries blocking other queries from executing."
227
+ how: "Analyzes pg_locks to find lock chains."
228
+ nuances:
229
+ - "Locks are a normal part of operation, but long locks are problematic."
230
+ - "Common causes: long transactions, missing indexes on UPDATE/DELETE with FK."
231
+ - "pg_cancel_backend(pid) cancels query, pg_terminate_backend(pid) terminates connection."
232
+ - "Consider optimistic locking at the application level."
233
+
234
+ locks:
235
+ title: "Locks"
236
+ what: "Current database locks."
237
+ how: "Data from pg_locks shows all active locks of all types."
238
+ nuances:
239
+ - "Types: AccessShare (SELECT), RowShare (SELECT FOR UPDATE), RowExclusive (INSERT/UPDATE/DELETE), AccessExclusive (ALTER TABLE, DROP)."
240
+ - "granted = false means the query is waiting for a lock."
241
+ - "deadlock_timeout controls how long before PostgreSQL checks for deadlock."
242
+
243
+ idle_connections:
244
+ title: "Idle Connections"
245
+ what: "Connections in idle state (not executing queries)."
246
+ how: "Filters pg_stat_activity by state = 'idle'."
247
+ nuances:
248
+ - "A moderate number of idle connections is normal for connection pooling."
249
+ - "Too many idle = application not closing connections or pool is too large."
250
+ - "idle_session_timeout (PostgreSQL 14+) can automatically close idle connections."
251
+
252
+ # === SYSTEM ===
253
+ database_sizes:
254
+ title: "Database Sizes"
255
+ what: "Sizes of all databases on the server."
256
+ how: "Uses pg_database_size() for each database."
257
+ nuances:
258
+ - "Size includes all tables, indexes, TOAST, and temporary objects."
259
+ - "Physically deleting data doesn't immediately reduce size — VACUUM FULL is needed."
260
+ - "Monitor growth for disk space planning."
261
+
262
+ settings:
263
+ title: "PostgreSQL Settings"
264
+ what: "Current PostgreSQL configuration settings."
265
+ how: "Reads pg_settings to show runtime configuration."
266
+ nuances:
267
+ - "Some parameters require restart (context = 'postmaster')."
268
+ - "Check source — where the value came from (default, configuration file, override)."
269
+ - "Use ALTER SYSTEM for permanent changes."
270
+
271
+ extensions:
272
+ title: "Extensions"
273
+ what: "Installed PostgreSQL extensions."
274
+ how: "List from pg_extension."
275
+ nuances:
276
+ - "pg_stat_statements — must have for performance monitoring."
277
+ - "pg_trgm — for LIKE '%pattern%' queries."
278
+ - "btree_gin/btree_gist — for combined GIN/GiST indexes."
279
+ - "Check version compatibility when upgrading PostgreSQL."
280
+
281
+ activity_overview:
282
+ title: "Activity Overview"
283
+ what: "Overall summary of current database activity."
284
+ how: "Aggregation of pg_stat_activity: active/idle/waiting queries, transactions."
285
+ nuances:
286
+ - "Useful for quick assessment of database state."
287
+ - "Compare with baseline to identify anomalies."
288
+
289
+ cache_stats:
290
+ title: "Cache Stats"
291
+ what: "Database caching statistics."
292
+ how: "Data from pg_stat_database shows blks_read vs blks_hit."
293
+ nuances:
294
+ - "Cache hit ratio = blks_hit / (blks_hit + blks_read)."
295
+ - "Target value: >99% for OLTP, >95% for mixed workload."
296
+ - "Low cache hit: increase shared_buffers (up to 25% RAM), or the problem is in queries."
297
+
298
+ # Problem explanations for highlighting
299
+ problems:
300
+ high_mean_time: "High mean execution time. Consider adding indexes or optimizing the query."
301
+ high_calls: "Very frequent calls. Consider caching or batch operations."
302
+ high_total_time: "Query consumes a lot of total time. Priority for optimization."
303
+ low_cache_hit: "Low cache hit ratio. Query frequently reads from disk instead of cache."
304
+ high_seq_scan: "Many sequential scans. Possibly missing an index."
305
+ unused_index: "Index is not used. Candidate for removal."
306
+ high_bloat: "High bloat. REINDEX or VACUUM required."
307
+ many_dead_tuples: "Many dead tuples. VACUUM required."
308
+ long_running: "Long-running query. May block other operations."
309
+ blocking: "Blocking other queries. Requires attention."
310
+ idle_in_transaction: "Open transaction without activity. Blocks VACUUM and holds locks."
@@ -0,0 +1,310 @@
1
+ ru:
2
+ pg_reports:
3
+ documentation:
4
+ # === QUERIES ===
5
+ slow_queries:
6
+ title: "Slow Queries"
7
+ what: "Запросы с высоким средним временем выполнения (mean_time)."
8
+ how: "Данные собираются из pg_stat_statements — расширения PostgreSQL, которое отслеживает статистику выполнения всех SQL-запросов. Среднее время рассчитывается как total_time / calls."
9
+ nuances:
10
+ - "Планировщик PostgreSQL может НЕ использовать индексы для небольших таблиц (< 10-50 тыс. строк), если sequential scan быстрее — это нормальное поведение."
11
+ - "Высокое среднее время может быть вызвано: отсутствием индекса, неоптимальным планом, блокировками, нагрузкой на диск."
12
+ - "Для комплексных индексов важен порядок колонок — индекс (a, b) эффективен для WHERE a = ? и WHERE a = ? AND b = ?, но не для WHERE b = ?."
13
+ - "Запросы с LIKE '%pattern%' не могут использовать B-tree индексы — рассмотрите pg_trgm или полнотекстовый поиск."
14
+
15
+ heavy_queries:
16
+ title: "Heavy Queries"
17
+ what: "Наиболее часто вызываемые запросы (по количеству вызовов)."
18
+ how: "Подсчитывается поле calls из pg_stat_statements. Высокое число вызовов увеличивает общую нагрузку на базу, даже если каждый отдельный запрос быстрый."
19
+ nuances:
20
+ - "Частые запросы — первые кандидаты на кэширование на уровне приложения (Redis, Memcached)."
21
+ - "N+1 проблема: если видите много похожих запросов с разными ID — это признак N+1, нужен eager loading."
22
+ - "Рассмотрите batch-операции вместо множества одиночных запросов."
23
+ - "Prepared statements могут снизить накладные расходы на парсинг для частых запросов."
24
+
25
+ expensive_queries:
26
+ title: "Expensive Queries"
27
+ what: "Запросы, потребляющие наибольшее суммарное время (total_time = mean_time × calls)."
28
+ how: "Сортировка по total_time показывает запросы с наибольшим влиянием на производительность базы в целом."
29
+ nuances:
30
+ - "Запрос может быть быстрым, но вызываться так часто, что суммарно занимает много времени."
31
+ - "Оптимизация expensive queries даёт наибольший эффект для общей производительности системы."
32
+ - "Рассмотрите: добавление индексов, переписывание запроса, кэширование результатов, материализованные представления."
33
+
34
+ missing_index_queries:
35
+ title: "Missing Index Queries"
36
+ what: "Запросы, выполняющие sequential scan на больших таблицах вместо использования индексов."
37
+ how: "Анализируется количество seq_scan и seq_tup_read для таблиц, затронутых запросами. Большое число прочитанных строк при seq_scan указывает на потенциальную необходимость индекса."
38
+ nuances:
39
+ - "Sequential scan НОРМАЛЕН для: маленьких таблиц, запросов возвращающих >5-10% данных, таблиц без подходящих колонок для индексации."
40
+ - "Комплексный индекс (a, b, c) покрывает: WHERE a, WHERE a AND b, WHERE a AND b AND c, но НЕ покрывает WHERE b или WHERE c."
41
+ - "EXPLAIN ANALYZE покажет реальный план выполнения запроса."
42
+ - "Partial indexes (CREATE INDEX ... WHERE condition) экономят место и ускоряют специфичные запросы."
43
+
44
+ low_cache_hit_queries:
45
+ title: "Low Cache Hit Queries"
46
+ what: "Запросы с низким процентом попадания в кэш (shared_buffers)."
47
+ how: "Рассчитывается как shared_blks_hit / (shared_blks_hit + shared_blks_read). Низкий cache hit ratio означает частое чтение с диска."
48
+ nuances:
49
+ - "Целевой cache hit ratio: >95% для активных данных."
50
+ - "Низкий cache hit может указывать на: недостаточный shared_buffers, слишком большие таблицы, неэффективные запросы сканирующие много данных."
51
+ - "Увеличение shared_buffers помогает, но есть предел эффективности (обычно 25% RAM)."
52
+ - "Covering indexes (INCLUDE) позволяют выполнять index-only scan без обращения к таблице."
53
+
54
+ all_queries:
55
+ title: "All Queries"
56
+ what: "Полная статистика по всем запросам из pg_stat_statements."
57
+ how: "Отображает все отслеживаемые запросы с их метриками: calls, total_time, mean_time, rows и т.д."
58
+ nuances:
59
+ - "pg_stat_statements нормализует запросы, заменяя литералы на $1, $2 и т.д."
60
+ - "Статистика сбрасывается при: перезапуске PostgreSQL, вызове pg_stat_statements_reset(), достижении лимита track_activity_query_size."
61
+ - "Параметр pg_stat_statements.max контролирует максимальное число отслеживаемых запросов (по умолчанию 5000)."
62
+
63
+ # === INDEXES ===
64
+ unused_indexes:
65
+ title: "Unused Indexes"
66
+ what: "Индексы, которые редко или никогда не используются для сканирования."
67
+ how: "Анализируется idx_scan из pg_stat_user_indexes. Индексы с нулевым или очень низким числом сканирований — кандидаты на удаление."
68
+ nuances:
69
+ - "Перед удалением убедитесь, что статистика собрана за достаточный период (включая все типы нагрузки: отчёты, batch-операции)."
70
+ - "Индексы всё равно обновляются при INSERT/UPDATE/DELETE, даже если не используются для чтения — это overhead."
71
+ - "Уникальные индексы (UNIQUE) могут иметь 0 сканирований, но нужны для constraint."
72
+ - "Foreign key индексы важны для производительности DELETE/UPDATE родительских таблиц."
73
+
74
+ duplicate_indexes:
75
+ title: "Duplicate Indexes"
76
+ what: "Индексы, которые дублируют другие индексы (один является префиксом другого)."
77
+ how: "Сравниваются определения индексов. Индекс (a) избыточен при наличии (a, b). Также выявляются полные дубликаты."
78
+ nuances:
79
+ - "Дубликаты тратят дисковое пространство и замедляют INSERT/UPDATE/DELETE."
80
+ - "Однако иногда 'дубликат' нужен: разные типы индексов (B-tree vs GIN), partial index vs full index."
81
+ - "Индекс (a, b) НЕ полностью заменяет (b, a) — порядок колонок критичен."
82
+
83
+ invalid_indexes:
84
+ title: "Invalid Indexes"
85
+ what: "Индексы, создание которых не завершилось успешно (invalid state)."
86
+ how: "Проверяется флаг indisvalid в pg_index. False означает, что индекс не будет использоваться планировщиком."
87
+ nuances:
88
+ - "Обычно появляются после неудачного CREATE INDEX CONCURRENTLY."
89
+ - "Решение: DROP INDEX и повторное создание."
90
+ - "REINDEX CONCURRENTLY может помочь для существующих невалидных индексов (PostgreSQL 12+)."
91
+
92
+ missing_indexes:
93
+ title: "Missing Indexes"
94
+ what: "Таблицы с высоким числом sequential scan, которым, вероятно, не хватает индексов."
95
+ how: "Анализируется соотношение seq_scan vs idx_scan и количество прочитанных строк (seq_tup_read)."
96
+ nuances:
97
+ - "Не все seq_scan плохи — для маленьких таблиц это оптимально."
98
+ - "Анализируйте конкретные запросы через pg_stat_statements, чтобы понять какие колонки индексировать."
99
+ - "Используйте EXPLAIN ANALYZE для проверки, будет ли индекс использоваться."
100
+
101
+ index_usage:
102
+ title: "Index Usage"
103
+ what: "Статистика использования индексов: сколько раз сканировались, сколько строк возвращали."
104
+ how: "Данные из pg_stat_user_indexes показывают эффективность каждого индекса."
105
+ nuances:
106
+ - "idx_scan — количество index scan операций."
107
+ - "idx_tup_read — строки прочитанные из индекса."
108
+ - "idx_tup_fetch — строки извлечённые из таблицы после index scan (для non-covering indexes)."
109
+
110
+ bloated_indexes:
111
+ title: "Bloated Indexes"
112
+ what: "Индексы с высоким уровнем bloat (раздувания) из-за мёртвых кортежей."
113
+ how: "Оценивается отношение реального размера к ожидаемому. Bloat появляется после UPDATE/DELETE операций."
114
+ nuances:
115
+ - "REINDEX или REINDEX CONCURRENTLY пересоздаёт индекс без bloat."
116
+ - "Регулярный VACUUM поддерживает bloat на приемлемом уровне."
117
+ - "pg_repack позволяет перестроить индексы без блокировок."
118
+ - "Bloat >30-50% — повод для действий."
119
+
120
+ index_sizes:
121
+ title: "Index Sizes"
122
+ what: "Размеры индексов на диске."
123
+ how: "Показывает pg_relation_size для каждого индекса."
124
+ nuances:
125
+ - "Большие индексы требуют больше памяти для эффективного кэширования."
126
+ - "Partial indexes могут значительно уменьшить размер: CREATE INDEX ... WHERE active = true."
127
+ - "BRIN индексы намного компактнее B-tree для некоторых паттернов данных (например, timestamp колонки)."
128
+
129
+ # === TABLES ===
130
+ table_sizes:
131
+ title: "Table Sizes"
132
+ what: "Размеры таблиц на диске (данные + TOAST + индексы)."
133
+ how: "Использует pg_total_relation_size для полного размера включая все связанные объекты."
134
+ nuances:
135
+ - "TOAST хранит большие значения (>2KB) в отдельной таблице."
136
+ - "Партиционирование помогает управлять большими таблицами."
137
+ - "Регулярный VACUUM FULL может уменьшить размер, но блокирует таблицу."
138
+
139
+ bloated_tables:
140
+ title: "Bloated Tables"
141
+ what: "Таблицы с высоким процентом dead tuples (мёртвых строк)."
142
+ how: "Анализируется n_dead_tup из pg_stat_user_tables. Dead tuples появляются после UPDATE/DELETE."
143
+ nuances:
144
+ - "VACUUM удаляет dead tuples и освобождает место для повторного использования."
145
+ - "VACUUM FULL физически уменьшает размер файла, но блокирует таблицу."
146
+ - "autovacuum должен справляться автоматически — проверьте его настройки если видите много bloat."
147
+ - "Dead tuple ratio >20% — сигнал проблемы с vacuum."
148
+
149
+ vacuum_needed:
150
+ title: "Vacuum Needed"
151
+ what: "Таблицы, которым требуется VACUUM на основе статистики dead tuples и времени последнего vacuum."
152
+ how: "Комбинирует данные о dead tuples, последнем vacuum и размере таблицы."
153
+ nuances:
154
+ - "last_vacuum и last_autovacuum показывают когда последний раз запускался vacuum."
155
+ - "Если autovacuum не справляется, можно: снизить autovacuum_vacuum_threshold, увеличить autovacuum_vacuum_scale_factor, добавить workers."
156
+ - "Для критичных таблиц можно настроить индивидуальные параметры: ALTER TABLE SET (autovacuum_vacuum_threshold = 1000)."
157
+
158
+ row_counts:
159
+ title: "Row Counts"
160
+ what: "Приблизительное количество строк в таблицах."
161
+ how: "Использует n_live_tup из pg_stat_user_tables (обновляется ANALYZE) или reltuples из pg_class."
162
+ nuances:
163
+ - "Это приблизительная оценка, точный COUNT(*) может отличаться."
164
+ - "ANALYZE обновляет статистику, autovacuum запускает его автоматически."
165
+ - "Для точного подсчёта используйте SELECT COUNT(*), но это может быть медленно на больших таблицах."
166
+
167
+ cache_hit_ratios:
168
+ title: "Cache Hit Ratios"
169
+ what: "Процент попадания в кэш (shared buffers) для каждой таблицы."
170
+ how: "Рассчитывается как heap_blks_hit / (heap_blks_hit + heap_blks_read)."
171
+ nuances:
172
+ - "Целевое значение: >95% для активно используемых таблиц."
173
+ - "Низкий cache hit: таблица слишком большая для кэша или редко используется."
174
+ - "Решения: увеличить shared_buffers, оптимизировать запросы, добавить индексы для уменьшения сканируемых данных."
175
+
176
+ seq_scans:
177
+ title: "Sequential Scans"
178
+ what: "Таблицы с высоким количеством sequential scan операций."
179
+ how: "Данные из pg_stat_user_tables показывают seq_scan vs idx_scan."
180
+ nuances:
181
+ - "seq_scan не всегда плохо — для маленьких таблиц это оптимально."
182
+ - "seq_tup_read показывает сколько строк прочитано — это важнее чем количество scan."
183
+ - "Высокий seq_tup_read/seq_scan = много строк за один scan = возможно норма."
184
+ - "Низкий seq_tup_read/seq_scan = много scan маленьких объёмов = возможно N+1."
185
+
186
+ recently_modified:
187
+ title: "Recently Modified"
188
+ what: "Таблицы с недавней активностью INSERT/UPDATE/DELETE."
189
+ how: "Анализируются n_tup_ins, n_tup_upd, n_tup_del из pg_stat_user_tables."
190
+ nuances:
191
+ - "Помогает понять паттерны записи в базу."
192
+ - "Высокая активность UPDATE создаёт dead tuples — следите за vacuum."
193
+ - "HOT updates (Heap Only Tuple) эффективнее обычных — индексы не обновляются."
194
+
195
+ # === CONNECTIONS ===
196
+ active_connections:
197
+ title: "Active Connections"
198
+ what: "Текущие подключения к базе данных."
199
+ how: "Данные из pg_stat_activity показывают все активные backend процессы."
200
+ nuances:
201
+ - "Каждое подключение потребляет память (~10MB per connection)."
202
+ - "max_connections ограничивает общее число — не устанавливайте слишком высоко."
203
+ - "Используйте connection pooler (PgBouncer, pgpool-II) для масштабирования."
204
+ - "state: active = выполняет запрос, idle = ждёт, idle in transaction = опасно."
205
+
206
+ connection_stats:
207
+ title: "Connection Stats"
208
+ what: "Статистика подключений сгруппированная по состоянию (state)."
209
+ how: "Агрегация pg_stat_activity по полю state."
210
+ nuances:
211
+ - "idle in transaction: транзакция открыта но не активна — блокирует VACUUM, удерживает локи."
212
+ - "idle in transaction (aborted): ошибка в транзакции, ждёт ROLLBACK."
213
+ - "Настройте idle_in_transaction_session_timeout для автоматического завершения."
214
+
215
+ long_running_queries:
216
+ title: "Long Running Queries"
217
+ what: "Запросы, выполняющиеся дольше заданного порога."
218
+ how: "Фильтрация pg_stat_activity по duration = now() - query_start."
219
+ nuances:
220
+ - "Долгие запросы могут держать блокировки и мешать другим процессам."
221
+ - "statement_timeout может автоматически прерывать слишком долгие запросы."
222
+ - "Для отчётов/аналитики используйте read replica или настройте отдельный connection pool."
223
+
224
+ blocking_queries:
225
+ title: "Blocking Queries"
226
+ what: "Запросы, которые блокируют выполнение других запросов."
227
+ how: "Анализ pg_locks для поиска цепочек блокировок."
228
+ nuances:
229
+ - "Блокировки — нормальная часть работы, но долгие блокировки проблематичны."
230
+ - "Частые причины: длинные транзакции, отсутствующие индексы при UPDATE/DELETE по FK."
231
+ - "pg_cancel_backend(pid) отменяет запрос, pg_terminate_backend(pid) завершает соединение."
232
+ - "Рассмотрите оптимистичную блокировку на уровне приложения."
233
+
234
+ locks:
235
+ title: "Locks"
236
+ what: "Текущие блокировки в базе данных."
237
+ how: "Данные из pg_locks показывают все активные блокировки всех типов."
238
+ nuances:
239
+ - "Типы: AccessShare (SELECT), RowShare (SELECT FOR UPDATE), RowExclusive (INSERT/UPDATE/DELETE), AccessExclusive (ALTER TABLE, DROP)."
240
+ - "granted = false означает, что запрос ждёт блокировку."
241
+ - "deadlock_timeout контролирует через сколько PostgreSQL проверит на deadlock."
242
+
243
+ idle_connections:
244
+ title: "Idle Connections"
245
+ what: "Подключения в состоянии idle (не выполняют запросы)."
246
+ how: "Фильтрация pg_stat_activity по state = 'idle'."
247
+ nuances:
248
+ - "Умеренное количество idle соединений — норма для connection pooling."
249
+ - "Слишком много idle = приложение не закрывает соединения или пул слишком большой."
250
+ - "idle_session_timeout (PostgreSQL 14+) может автоматически закрывать idle соединения."
251
+
252
+ # === SYSTEM ===
253
+ database_sizes:
254
+ title: "Database Sizes"
255
+ what: "Размеры всех баз данных на сервере."
256
+ how: "Использует pg_database_size() для каждой базы."
257
+ nuances:
258
+ - "Размер включает все таблицы, индексы, TOAST и временные объекты."
259
+ - "Физическое удаление данных не сразу уменьшает размер — нужен VACUUM FULL."
260
+ - "Мониторьте рост для планирования дискового пространства."
261
+
262
+ settings:
263
+ title: "PostgreSQL Settings"
264
+ what: "Текущие настройки конфигурации PostgreSQL."
265
+ how: "Чтение pg_settings показывает runtime конфигурацию."
266
+ nuances:
267
+ - "Некоторые параметры требуют перезапуска (context = 'postmaster')."
268
+ - "Проверяйте source — откуда взято значение (default, configuration file, override)."
269
+ - "Используйте ALTER SYSTEM для постоянных изменений."
270
+
271
+ extensions:
272
+ title: "Extensions"
273
+ what: "Установленные расширения PostgreSQL."
274
+ how: "Список из pg_extension."
275
+ nuances:
276
+ - "pg_stat_statements — must have для мониторинга производительности."
277
+ - "pg_trgm — для LIKE '%pattern%' запросов."
278
+ - "btree_gin/btree_gist — для комбинированных GIN/GiST индексов."
279
+ - "Проверяйте совместимость версий при обновлении PostgreSQL."
280
+
281
+ activity_overview:
282
+ title: "Activity Overview"
283
+ what: "Общая сводка текущей активности базы данных."
284
+ how: "Агрегация pg_stat_activity: активные/idle/waiting запросы, транзакции."
285
+ nuances:
286
+ - "Полезно для быстрой оценки состояния базы."
287
+ - "Сравнивайте с baseline для выявления аномалий."
288
+
289
+ cache_stats:
290
+ title: "Cache Stats"
291
+ what: "Статистика кэширования базы данных."
292
+ how: "Данные из pg_stat_database показывают blks_read vs blks_hit."
293
+ nuances:
294
+ - "Cache hit ratio = blks_hit / (blks_hit + blks_read)."
295
+ - "Целевое значение: >99% для OLTP, >95% для mixed workload."
296
+ - "Низкий cache hit: увеличьте shared_buffers (до 25% RAM), или проблема в запросах."
297
+
298
+ # Problem explanations for highlighting
299
+ problems:
300
+ high_mean_time: "Высокое среднее время выполнения. Рассмотрите добавление индексов или оптимизацию запроса."
301
+ high_calls: "Очень частые вызовы. Рассмотрите кэширование или batch-операции."
302
+ high_total_time: "Запрос потребляет много общего времени. Приоритет для оптимизации."
303
+ low_cache_hit: "Низкий cache hit ratio. Запрос часто читает с диска вместо кэша."
304
+ high_seq_scan: "Много sequential scan. Возможно не хватает индекса."
305
+ unused_index: "Индекс не используется. Кандидат на удаление."
306
+ high_bloat: "Высокий bloat. Требуется REINDEX или VACUUM."
307
+ many_dead_tuples: "Много мёртвых строк. Требуется VACUUM."
308
+ long_running: "Долго выполняющийся запрос. Может блокировать другие операции."
309
+ blocking: "Блокирует другие запросы. Требует внимания."
310
+ idle_in_transaction: "Открытая транзакция без активности. Блокирует VACUUM и удерживает локи."