СУБД PostgreSQL — это огромный механизм, который состоит из множества подсистем, чья работа определяет производительность PostgreSQL. В процессе эксплуатации обеспечивается сбор статистики и информации о работе компонентов, что позволяет оценить эффективность PostgreSQL и принять меры для повышения производительности. Однако, этой информации очень много и представлена она в достаточно упрощенном виде. Обработка этой информации и ее интерпретация порой совсем нетривиальная задача, а зоопарк инструментов и утилит запросто поставит в тупик даже продвинутого DBA.
В докладе речь пойдет о подсистеме сбора статистики, о том какая информация доступна для оценки эффективности PostgreSQL, как её получить, не прибегая к зоопарку инструментов. Как интерпретировать и использовать полученную информацию, как найти узкие места, устранить их и повысить производительность PostgreSQL.
4. Итоговая цель
Умение использовать статистику это полезно.
Статистика в постгресе это не страшно.
Какую статистику выбрать для решения частной
задачи.
6. Как тратится время
Write Ahead Log
Shared
Buffers
Buffers IO Autovacuum Workers
Autovacuum Launcher
Background Workers
Indexes IO
Query Execution
Query Planning
Client Backends Postmaster
Relations IO
Logger Process Stats Collector
Logical
Replication
WAL Sender
Process
Archiver
Process
Background
Writer
Checkpointer
Process
Network Storage
Recovery Process
WAL Receiver Process
Tables/Indexes Data Files
7. Проблемы
Информации много (~109 метрик для 9.4).
Статистика представлена online счетчиками.
Нет истории (но есть функции сброса статистики).
Отсутствие native инструмента.
Множество сторонних инструментов.
8. Проблемы
Информации много (~109 метрик для 9.4).
Статистика представлена online счетчиками.
Нет истории (но есть функции сброса статистики).
Отсутствие native инструмента.
Множество сторонних инструментов.
Важно уметь брать статистику напрямую.
Нужны базовые знания SQL.
14. Cache hit ratio
$ select * from pg_stat_database;
...
blks_read | 7978770895
blks_hit | 9683551077519
...
$ select
sum(blks_hit)*100/sum(blks_hit+blks_read) as hit_ratio
from pg_stat_database;
Больше = лучше, но не меньше 90%
24. Sequential scans
$ select * from pg_stat_all_tables;
...
seq_scan | 192 необязательно плохо
seq_tup_read | 364544695 > 1000 (seq_tup_avg)
...
$ select
relname,
pg_size_pretty(pg_relation_size(relname::regclass)) as size,
seq_scan, seq_tup_read,
seq_scan / seq_tup_read as seq_tup_avg
from pg_stat_user_tables
where seq_tup_read > 0 order by 3,4 desc limit 5;
25. Размеры таблиц
$ select
relname,
pg_size_pretty(pg_total_relation_size(relname::regclass)) as
full_size,
pg_size_pretty(pg_relation_size(relname::regclass)) as
table_size,
pg_size_pretty(pg_total_relation_size(relname::regclass) -
pg_relation_size(relname::regclass)) as index_size
from pg_stat_user_tables
order by pg_total_relation_size(relname::regclass) desc limit 10;
Также есть метакоманды psql: dt+ и di+
27. Write activity
$ select
s.relname,
pg_size_pretty(pg_relation_size(relid)),
coalesce(n_tup_ins,0) + 2 * coalesce(n_tup_upd,0) -
coalesce(n_tup_hot_upd,0) + coalesce(n_tup_del,0) AS total_writes,
(coalesce(n_tup_hot_upd,0)::float * 100 / (case when n_tup_upd > 0
then n_tup_upd else 1 end)::float)::numeric(10,2) AS hot_rate,
(select v[1] FROM regexp_matches(reloptions::text,E'fillfactor=(d+)') as
r(v) limit 1) AS fillfactor
from pg_stat_all_tables s
join pg_class c ON c.oid=relid
order by total_writes desc limit 50;
Что такое Heap-Only Tuples?
HOT не вызывает перестроения индекса.
HOT только для тех значений которые не участвуют в индексе
Большое значение n_tup_hot_upd = хорошо.
Как добиться большого n_tup_hot_upd?
28. Write activity
| pg_size_pretty | total_writes | hot_rate | fillfactor
-+----------------+--------------+----------+------------
| 9418 MB | 391459091 | 5.03 |
| 417 MB | 285948760 | 0.00 |
| 76 MB | 232031972 | 87.31 | 70
| 7123 MB | 124281107 | 99.36 | 70
Fillfactor определяет резерв свободного места в странице.
Накладные расходы на размер таблиц (индексов).
ALTER TABLE table_name SET (fillfactor = 70);
38. Долгие запросы
$ select * from pg_stat_activity;
...
backend_start | 2015-10-14 15:18:03.01039+00
xact_start | 2015-10-14 15:21:15.336325+00
query_start | 2015-10-14 15:21:30.336325+00
state_change | 2015-10-14 15:21:30.33635+00
...
$ select
client_addr, usename, datname,
clock_timestamp() - xact_start as xact_age,
clock_timestamp() - query_start as query_age,
query
from pg_stat_activity order by xact_start, query_start;
39. Долгие запросы
$ select * from pg_stat_activity;
...
backend_start | 2015-10-14 15:18:03.01039+00
xact_start | 2015-10-14 15:21:15.336325+00
query_start | 2015-10-14 15:21:30.336325+00
state_change | 2015-10-14 15:21:30.33635+00
...
$ select
client_addr, usename, datname,
clock_timestamp() - xact_start as xact_age,
clock_timestamp() - query_start as query_age,
query
from pg_stat_activity order by xact_start, query_start;
clock_timestamp() для вычисления времени работы.
Запросы запоминаем, отстреливаем, оптимизируем (EXPLAIN).
40. Плохие транзакции
$ select * from pg_stat_activity where state in
('idle in transaction', 'idle in transaction (aborted)';
...
xact_start | 2015-10-14 15:21:21.128192+00
query_start | 2015-10-14 15:21:30.336325+00
state_change | 2015-10-14 15:21:30.33635+00
state | idle in transaction
...
41. Плохие транзакции
$ select * from pg_stat_activity where state in
('idle in transaction', 'idle in transaction (aborted)';
...
xact_start | 2015-10-14 15:21:21.128192+00
query_start | 2015-10-14 15:21:30.336325+00
state_change | 2015-10-14 15:21:30.33635+00
state | idle in transaction
...
idle in transaction, idle in transaction (aborted) = плохо
Повод для беспокойств: > 5
clock_timestamp() для вычисления времени работы.
Транзакции отстреливаем, приложение оптимизируем.
42. Блокировки
$ select * from pg_stat_activity where waiting;
...
xact_start | 2015-10-14 15:21:21.128192+00
query_start | 2015-10-14 15:21:30.336325+00
state_change | 2015-10-14 15:21:30.33635+00
waiting | t
...
43. Блокировки
$ select * from pg_stat_activity where waiting;
...
xact_start | 2015-10-14 15:21:21.128192+00
query_start | 2015-10-14 15:21:30.336325+00
state_change | 2015-10-14 15:21:30.33635+00
waiting | t
...
waiting = true = плохо.
clock_timestamp() для вычисления времени работы.
pg_locks для поиска источника блокировки.
Транзакции отстреливаем, приложение оптимизируем.
46. pg_stat_statements
$ select * from pg_stat_statements;
...
query | SELECT "id" FROM run_plan_xact(?)
calls | 11165832
total_time | 11743325.6880088
rows | 11165832
blk_read_time | 495425.535999976
blk_write_time | 0
Cреднее время запроса в милисекундах
$ select (sum(total_time) / sum(calls))::numeric(6,3)
from pg_stat_statements;
Самые активно пишущие (в shared_buffers) запросы
$ select query, shared_blks_dirtied
from pg_stat_statements
where shared_blks_dirtied > 0 order by 2 desc;
47. Отчеты
query total time: 15:43:07 (14.9%, CPU: 18.2%, IO: 9.0%)
сalls: 476 (0.00%) rows: 476,000
avg_time: 118881.54ms (IO: 21.2%)
user: app_user db: ustats
query: select
filepath, type, deviceuid
from imv5event
where
state = ?::eventstate
and servertime between $1 and $2
order by servertime desc LIMIT $3 OFFSET $4
https://github.com/PostgreSQL-Consulting/pg-utils/blob/master/sql/glob
al_reports/query_stat_total.sql
48. Отчеты
query total time: 15:43:07 (14.9%, CPU: 18.2%, IO: 9.0%)
сalls: 476 (0.00%) rows: 476,000
avg_time: 118881.54ms (IO: 21.2%)
user: app_user db: ustats
query: select
filepath, type, deviceuid
from imv5event
where
state = ?::eventstate
and servertime between $1 and $2
order by servertime desc LIMIT $3 OFFSET $4
Используем sum() для подсчет общей статистики.
Вычисляем «вклад» запроса в общую статистику.
Использование ресурсов (CPU, IO).
49. За кадром
pg_statio_all_tables, pg_statio_all_indexes
pg_stat_user_functions
Функции определения размеров - df *size*
pgstattuple (contrib)
●
точное определение bloat для индексов и таблиц
●
чем больше размер таблицы, тем больше времени уйдет на
оценку
pg_buffercache (contrib)
●
инспекция shared buffers
●
большие накладые расходы (buffers lock)
50. За кадром
pgfincore
●
низкоуровневые операции с таблицами через mincore().
●
инспекция OS page cache.
pg_stat_kcache
●
использование getrusage() до и после запроса.
●
cpu usage и дисковый ввод-вывод.
●
требует pg_stat_statements и postgresql-9.4.
●
почти не влияет на производительность.