
Привет! Меня зовут Дмитрий Руденко, я из команды Databases Т-Банка.В последние годы наблюдается тенденция к переходу на Postgres со стороны многих команд и компаний, что приводит к увеличению количества Postgres-баз данных, требующих эффективного мониторинга и управления.
Мы достигли впечатляющего масштаба — почти 10 000 работающих экземпляров PostgreSQL, с которыми работают более 2 000 команд. Каждый из этих инстансов обслуживает уникальные рабочие нагрузки, разработанные командами с различными подходами к архитектуре, используемыми фреймворками и паттернами проектирования. Несмотря на огромное количество БД, команда администраторов баз данных (DBA) остается относительно небольшой (10 человек). Это создает целый ряд сложных задач, требующих тщательного управления и оптимизации процессов.
В прошлом году мы завороженно следили за приближением цунами из миксов обслуживания, исследований, вопросов, загадок да еще к тому же щедро приправленных пугающими сбоями во всякого рода критичных системах.
Что делать, когда на тебя надвигается вал работы? Правильно, нужно ее кому-нибудь делегировать. Делегировать решили разработчикам. Они гораздо более сведущи в используемых ими подходах, знают больше об их структурах данных, знают узкие и критичные места. Надо просто помочь им увидеть, что же именно происходит в черном ящике БД Postgres.
Мы пришли к созданию общей системы мониторинга баз данных Postgres, предоставляющей пользователю в простом и понятном виде данные о состоянии инстанса. В этой статье я расскажу о нашем видении визуального представления информации для пользователя и о созданной нами группе дашбордов для наблюдения за базами данными Postgres. Вы увидите, что накопительная статистика Postgres совместно с Prometheus и Grafana способны творить чудеса.
Административный дашборд
Начнем, пожалуй, с дашборда, предназначенного для нас — администраторов всего парка Postgres, ибо он является отличной начальной точкой для экскурсии по всем нашим пунктам наблюдения за работой PostgreSQL.
На административном дашборде мы собираем все проблемные инстансы (под инстансом здесь и далее подразумевается кластер Patroni).

На дашборде видим сборную информацию обо всех инстансах, которые за выбранный период испытывали проблемы с доступностью или на которых произошли failover или switchover. Имена инстансов — линки для перехода на подробные дашборды.
Ниже по дашборду расположена панель с картой доступности хостов.


В таблице можно увидеть уровень критичности, имя инстанса (соответствует кластеру Patroni), хост и его роль, некоторые метрики и описания хоста. Суммарное время недоступности отображается в поле total_time. Очень удобно, что можно быстро перейти к детальным дашбордам по проблемному хосту. По клику на instance_name мы переходим в дашборд по всему кластеру (DB General), по клику на host — в детальный дашборд по хосту (Host details).
На отдельной панели можно увидеть ситуацию с failover и switchover инстансов.

Еще мы собрали топовые инстансы по утилизации ЦПУ, дискового пространства и распухшим wal. Эти секции помогают нам отслеживать инстансы с резкими деградациями производительности, инстансы, приближающиеся к дисковому лимиту, и инстансы с большими pg_wal-директориями (появляющимися чаще всего из-за залипших слотов репликации).

Раскрыта секция со списком хостов с высокой общей утилизацией ЦПУ. В таблице можно увидеть основные характеристики, роль хоста в кластере Patroni, текущую утилизацию ЦПУ. По кликам на имени хоста и инстанса можно перейти на детальные дашборды.
Администраторы могут быстро находить проблемные инстансы и переходить в дашборды с детальной информацией по ним. Эти дашборды предназначены также и для разработчиков, поэтому для них мы подготовили документацию. Для каждого графика есть ссылка на подробное описание и его особенности: на что обращать внимание, возможные кейсы и так далее.

Все эти дашборды организованы одинаково: слева — список меню для перехода по различным разделам, справа — графики.
Первый пункт меню — логи, без них не обходится ни одно расследование. Следом идет обзорный дашборд по всему кластеру.
Дашборд DB General
DB General показывает хосты и их роли в кластере Patroni, некоторые характеристики сервера, утилизации, wal, лаги репликации. Справа — график доступности. Слева подсвечивается, прямо на уровне меню (и, соответственно, этот сигнал виден на всех дашбордах), опасное приближение к исчерпанию доступного места.

Ниже по дашборду можно увидеть изменение ролей во времени и детальную информацию по хостам, представленным отдельными row. По каждому хосту можно видеть утилизации и сводные данные о нагрузках. Еще ниже — параметры pg_settings (для реплик показываются только расхождения с лидером)


В DB General представлена самая общая информация. Для более детального исследования предназначены еще несколько дашбордов.
Узкоспециализированные дашборды
Сначала очень кратко рассмотрим небольшие узкоспециализированные дашборды DB Connections, DB Size, Host details, Health map и Troubleshooting, а затем подробно мощный DB Performance.
Дашборд DbConnection показывает подключения к бд Postgres и некоторые метрики сессий.

Дашборд DbSize показывает размер базы и ее составляющих.

На дашборде видно процент общего занятого места от лимита, распределение занятого места по базам данных, графики занимаемого места по директориям (удобно отслеживать распухание логов и wal журналов), отдельно для каждого участника кластера Patroni. График динамики размеров топовых объектов базы данных.
Дашборд HostDetails — здесь отображается детальная информация о хостах кластера Patroni.

В Health map собраны различные индикаторы проблем с инстансом, а в Troubleshooting сконцентрирована только самая важная информация, помогающая определить источник проблемы во время сбоя. Они предназначены в основном для администраторов.


Дашборд Performance
Performance — вишенка на торте, большой и навороченный дашборд, главный помощник при определении узких мест с производительностью базы данных.

По умолчанию развернута секция Overview.

Overview позволяет одним взглядом оценить ситуацию в выбранный момент времени. Каждая панель состоит из индикатора, агрегирующего соответствующие метрики, и графика, на котором можно видеть динамику происходящего. На многие индикаторы настроены линки для перехода к просмотру логов. Например, при обнаружении дедлока можно кликнуть на красный индикатор и увидеть развернутую информацию о событии.


Индикаторы Maintenance помогают определить, выполнялись ли в указанном диапазоне времени какие-либо фоновые обслуживающие процессы, ручные запуски вакуума, не проводились ли тяжелые процессы создания пользователем индекса.

Когда мы исследуем какую-либо проблему, желательно сразу же видеть, что у нас творилось в этот момент в фоне. Если в это время работал «агрессивный» сборщик мусора, то с большой долей вероятности можно предположить, что именно он повлиял на общую просадку производительности.
Бросив взгляд на секцию Overview, мы по паттерну красных областей сразу понимаем, куда нужно копать. Ниже расположено много важных секций. На некоторых мы остановимся подробнее.
Секция Database — первая из свернутых по умолчанию секций дашборда Performance. В ней находится общая информация, нагрузки, ошибки, работа с диском, попадания в кэш и так далее. Метрики собираются с pg_stat_database.

Секция Sessions — одна из важнейших и полезных секций. В ней отражены метрики, собранные с представления pg_stat_activity.


Хорошо видно, как запустившийся автовакуум привел к росту общего времени проведенного в ожидании и количества сессий, ожидающих IO:DataFileRead.
Ну и главный свидетель (мое любимое в этой секции) — таблица активности сессий. Зачастую здесь можно обнаружить каких-либо нарушителей порядка.

Секция Locks — раздел, посвященный блокировкам. Метрики, собранные с представления pg_locks.

В тяжелых случаях, когда запрос находится в блокировке дольше минуты, внизу выстраивается дерево Lock tree, по которому легко можно определить, кто виновник всей этой вакханалии (узнать pid процесса, удерживающего корневую блокировку).
Секция Checkpoints & Bgwriter предназначена для наблюдения за процессами checkpoints и background writer (метрикам, собранным с представления pg_stat_bgwriter). Позволяет оценить работу и корректность настроек этих процессов.

Секция Wal позволяет оценивать интенсивность записи Wal журналов (метрики, собранные с представления pg_stat_wal) и их архивацию (pg_stat_archiver).

Секция Replication предоставляет важные данные о состоянии потоковой репликации и помогает выявлять потенциальные проблемы, связанные с репликацией.


Секция Vacuum & Analyze важна для наблюдения за процессами вакуума (метрики, собранные с представлений pg_stat_progress_vacuum, pg_stat_progress_analyze).
Postgres при обновлении записи создает новую версию. Старая версия какое-то время может быть видима для каких-то транзакций, но потом теряет актуальность и просто занимает место. Со временем таких строк становится все больше, и наступает момент, когда для добавления новой записи приходится создавать новую страницу.
Postgres может сам определять, когда пора вызывать сборщик мусора, или можно сделать это вручную. Сборщик убирает старые версии, но место при этом не освобождается, таблица остается такой же распухшей. Чтобы вернуть место на дисках операционной системе, используется vacuum full, который заново перестраивает всю таблицу. На дашборде с деталями по таблице (мы подробнее рассмотрим ее далее) хорошо видно все три этапа.

Раздел, посвященный сборке мусора, важен, потому что зачастую это довольно тяжелый процесс, который может сильно снизить производительность системы. Поэтому нам важно знать, когда он запускался и с какими таблицами работал.
Первый график показывает, какие таблицы подвергались сборке мусора.

Есть подробная таблица с временными отсчетами

Аналогичные графики и таблицы, только на этот раз отображающие процессы analyze, расположены после вакуума.

Секция Command progress. Еще один раздел мы решили создать после многочисленных историй о переживаниях пользователей по поводу длительности выполнения процессов, которые они запускают. Например, пользователь запускает процесс создания нового индекса и ждет… Что происходит? В каком состоянии процесс? Неизвестно. Уже несколько минут длится... Сброшу. И вот на 99% прогресса пользователь терминирует свой запрос. Эх.
Но теперь это в прошлом: если пользователь запустил какой-то длительный процесс, то в Command Progress он может посмотреть в реальном времени текущий прогресс, этап выполнения и возможных блокировщиков (метрики собраны с представлений pg_stat_progress_create_index, pg_stat_progress_cluster).

Запросы, таблицы, индексы
Рассмотрим три главные для анализа Performance секции и начнем с запросов.
Секция Queries предназначена для анализа общей утилизации и детальных метрик запросов (метрики собираются с представления pg_stat_statements).
Первым делом — график общей утилизации запросами. Обожаю стекируемые графики: ощущаешь себя геологом, изучающим слои почвы. Внизу видны останки динозавров, еще ниже — лава.

Потребление ресурсов складывается из нагрузки на процессор и систему ввода-вывода. С помощью переменной query_utilization мы можем выбрать другой срез.


Графики — это прекрасно, но у нас есть шикарная таблица, еще более удобный и мощный инструмент для поиска всяких несуразностей в запросах к базе данных. Прежде всего — благодаря возможности сортировки. Например, мы увидели всплеск по временным файлам, горит красный индикатор. Можно кликнуть на соответствующий столбец и вытащить запросы, которые больше всего использовали временные файлы.

В таблице общая утилизация, разбиение на CPU/IO, количество вызовов запроса за выбранный интервал времени, суммарное количество задействованных строк, среднее время выполнения запроса, ну и самые главные, на мой взгляд, метрики, описывающие эффективность доступа к данным, — количество страниц, которые обрабатывал Postgres во время выполнения запроса.
Часть из этих страниц была в кэше, а часть пришлось считать с диска (процент — столбец Hit/Read). Но в любом случае мы призываем разработчиков стремиться по возможности минимизировать общее количество задействованных страниц. Многие недооценивают важность этих значений.
На мой взгляд, метрики, описывающие количество прочитанных страниц, гораздо больше характеризует качество запроса, чем длительность выполнения, которую часто приводят как показатель его эффективности. Часто можно услышать: «Я оптимизировал запрос, он стал выполняться настолько-то миллисекунд быстрее». Мне кажется продуктивнее другой подход: «Я оптимизировал запрос, и он вместо 1 000 блоков стал собирать 10» — вот это действительно чувствительная оптимизация запроса.
В таблице у нас показаны среднее количество страниц за запрос и количество страниц на одну задействованную строку. Значение Blocks/row становится важным, когда запрос возвращает или модифицирует много строк. Если цена каждой возвращенной строки — 100 прочитанных страниц, то как минимум это должно привлечь внимание, все ли мы делаем оптимально. Если запрос с красными числами — надо что-то делать, если с оранжевыми — желательно тоже, с желтыми — надо обратить внимание. Для oltp-нагрузки (а именно такая преобладает у нас) мы установили такие цветовые thresholds.

Запросы бывают разные, и некоторым действительно необходимо прочитать много страниц (например агрегации), но в любом случае мы рекомендуем обращать пристальное внимание на метрики количества задействованных страниц (блоков), особенно для запросов, которые вызываются часто и находятся в топе по утилизации.
Под таблицей расположен еще один график, который как раз показывает количество прочитанных страниц на один запрос. Когда у запроса неожиданно ломается план, это хорошо заметно на этом графике.

Есть два подхода искать разовые тяжелые запросы потерявшиеся в общей утилизации:
Можно на графике выделить более узкий диапазон, в который попадает искомый запрос, и тогда он, скорее всего, выйдет в топ в таблице.
Кликнуть на сортировку и поднять наверх те запросы, которые хуже всего работают с блоками. «Нарушитель», скорее всего, тоже поднимется в топ.

В результате остальные запросы наверняка тоже чуть-чуть просели, потому что если они раньше попадали в кеш в 99% случаев, то сейчас стали попадать в 96—98%. Иногда эта разница оказывает весьма существенное влияние.
Определив проблемный запрос, мы можем кликнуть на него и перейти в отдельный дашборд, который посвящен конкретному запросу, — Query details.

На дашборде видим, что количество вызовов и число возвращаемых строк запроса практически не меняется. Но если посмотреть график времени его выполнения, то можно сразу увидеть признаки деградации.

Еще чуть ниже расположены графики количества считываемых страниц — там мы сразу видим причину подобной деградации.

Вернемся на дашборд DB Performance.
Секция Tables. Если секции Sessions и Queries представляют некий тактический взгляд на ситуацию с запросами, то секция Tables представляет в большей степени стратегический взгляд. Метрики для этой секции собираются (с представления pg_stat_user_tables) гораздо реже, чем остальные, — раз в час, но это отнюдь не делает их менее полезными.
Давайте разберем еще один случай слома плана, а заодно подробнее познакомимся с секцией Tables.
В этот раз слом плана был настолько суровым, что, несмотря на большое количество попыток, запрос не смог завершиться ни разу, падая с ошибкой (ERROR: temporary file size exceeds temp_file_limit). А раз он не завершился ни разу, то и в pg_stat_statements не попал и в queries его не видно.
Но у нас есть еще tables — попробуем найти что-нибудь там.
Ситуация сложилась так: неожиданно, примерно в 02:55, джоб, который до этого выполнялся за вполне вменяемое время, вдруг перестал завершаться. При этом на графиках утилизации видна такая картина:


Обращает на себя внимание резко просевший в этот момент коэффициент попадания в кэш и резкий рост количества временных файлов.
Откроем секцию Tables.

Нас интересовал график топ-20 самых активных таблиц.

Например, можно вывести общую статистику, только на чтение (по индексам или последовательно) или только на запись (все виды) и т. д. В данном случае обращает на себя внимание резко возросшее количество считанных последовательным сканированием строк (seq scan)
Под графиком топ-20 у нас есть вот такая шикарная таблица.

Тут самая важная информация с данными на момент окончания выбранного в графане диапазона или агрегированные за этот диапазон значения метрик: размеры таблицы, изменение размеров, оценка по количеству строк, число sec scan, которые были выполнены в этом промежутке времени, плюс количество считанных при этом строк. То же самое по index scan: свежесть статистики, близость к вызову autovacuum, живые и мертвые строки, суммарная инфа по модификационной активности за выбранный период, попадания в кэш и оценка блоатинга.
Как и в случае с Queries, очень удобно пользоваться сортировкой для вывода в топ таблиц с теми или иными проблемами. По умолчанию строки отсортированы на основании неких очков проблемности, так сказать таблицы для показа таблиц, требующих внимания пользователя.
На первое место у нас попала объемная таблица (0,5 млрд строк) по которой выполнено 289 sec scans (не удивительно, что она попала на первое место в рейтинге проблемности).
Естественно, такие объемы в основном забирались с диска — близкий к нулю процент попадания в кэш для тушки таблицы. Очевидно, что проблема связана с какими-то запросами к этой таблице.
В секции Sessions можно найти этот запрос — запрос, который выполнялся нормально, но неожиданно испортился примерно в 02:50.
select mp1_0.id, ....
from f.payment mp1_0
left join f.status_history psh1_0 on mp1_0.id=psh1_0.pay_id
where mp1_0.dt_create between $1 and $2
and mp1_0.state in ($3,$4,$5,$6,$7,$8....)
and mp1_0.gate_in in ($179)
В нем есть обращение к двум таблицам (master - detail) и проблемная (f.status_history) джойнится к мастеру f.payment.
Посмотрим, какую дополнительную информацию мы сможем найти для каждой из этих двух таблиц.
Да-да, есть еще один дашборд, в котором собраны метрики конкретной таблицы и ее индексов. Чтобы открыть его, надо, как обычно, кликнуть на имени проблемной таблицы.

Дашборд Table details сконцентрировал различные визуализации метрик какой-либо таблицы и ее индексов. Здесь очень удобно отслеживать динамику различных характеристик, активностей и обслуживающих таблицу процессов, а также их зависимостей друг от друга (например, соотносить динамику по модификационным изменениям и соответствующим вызовам vacuum/analyze).
Секция Overview собрала общую информацию о таблице и график размера таблицы, индексов и toast.

Чуть ниже идет секция, в которой собраны метрики по различным видам активности таблицы.
Секция Activity — важнейший график модификационной активности.

Графики активности на чтение

У проблемной таблицы обычно преобладают index scan, но в момент сбоя действительно наблюдались тяжелые seq scans (второй график).
Секция Vacuum Analyze Bloat предназначена для отслеживания процессов Vacuum для исследуемой таблицы и оценки его эффективности. Посмотрим, как она работает, на примере недельной партиции одной из таблиц.

Вернемся к проблемной таблице. Видно, что в указанный момент никакой активности по вакууму или analyze не было.

Секция Indexes — еще одна важнейшая для анализа перформанса секция. Тут можно посмотреть полный список индексов таблицы и, самое главное, оценить их эффективность!

В Indexes можно по графикам оценить использование индексов в динамике. Видно, что pk за выбранный диапазон не использовался ни разу, а вот второй индекс (foreign key на головную таблицу), напротив, был активен. 112k сканов, в среднем извлекалось 20 записей за скан.
Секция Queries. Последняя секция в этом дашборде — полный аналог (library panel) рассмотренной выше секции Queries в дашборде DB Performance за тем исключением, что показываются не все запросы, а только те, в которых так или иначе используется исследуемая таблица.

В нашем случае эта секция нам не поможет, так как проблемные запросы так и не попали в pg_stat_statements. По зависимой таблице ничего выяснить не удалось. Посмотрим теперь на метрики головной таблицы.

Мастер-таблица также приличного размера (25 млн строк), но тут уже преобладают апдейты, новых записей вставляется относительно немного. При чтении используются исключительно index scans — все корректно!

Откроем секцию Vacuum & Analyze.

Уже интересно: подозрительно близко к началу сбоя случился analyze таблицы.
Метрики собираются раз в час, и нужна более точная информация. Кликаем на столбик — открываются логи с фильтром на 'analyze' и именем таблицы за выбранный диапазон времени.


Аналайз случился прямо перед началом сбоя. Очевидно, он и привел к таким последствиям. Придется подключиться к хосту и внимательно изучить статистику по этой таблице.
Сам по себе этот кейс оказался достаточно интересным.
Из-за особенностей бизнес-логики, статистического распределения данных и формы запроса (в запросе передавалось в качестве параметров большое количество статусов, которые не попадали в most_common_vals) планировщик неверно оценивал количество возвращаемых из мастер-таблицы строк (2 млн вместо реальных 1—2 записей) и принимал ужасное решение использовать hash join вместо nested loop.
Естественно, для формирования хэш-таблицы postgres устраивал parallel sec scan для detail-таблицы, который так ни разу и не завершился из-за превышения temp_file_limit. Может быть, мы подробнее расскажем об этом и некоторых других интересных кейсах в отдельной статье.
Секция Partitions. Еще немного о представлении партиционированных таблиц в дашборде Table details. Если в качестве объекта исследования партиционированная таблица, то в дашборде все метрики, все статистики и графики представляют агрегированные значения по всем партициям, что достаточно удобно для оценки общей статистики сразу по всем партициям.

Но иногда хочется все же посмотреть на общую картину в деталях. Для этого у нас и предназначена секция Partitions.
В Partition нас ожидает знакомая уже панель (library panel) со статистикой по таблицам. Только на этот раз тут собраны исключительно партиции исследуемой таблицы.
Тут можно обратить внимание на большое количество index scan по прошлым партициям с практически нулевым выхлопом (idx_tup/scan). Это, скорее всего, говорит о том, что разработчики не указывают условия партиционирования в запросах (чаще всего встречается обновление по id. Postgres же не знает, в какой партиции лежит выбранный id, и вынужден сканировать их все).

Дополнительно ниже можно визуально в динамике оценить активность по всем партициям. Выбор вида активности также управляется переменной activity.
Можно, например, выбрать Write и убедиться, что основная модификационная активность в партиции происходит во время ее диапазона. Но устаревшие партиции все же модифицируются, хотя и не так активно.

Информация об индексах, представленная в Table details, требует еще одного глобального представления — сквозного, так сказать, по всему инстансу. Вернемся на дашборд DB Performance.
Секция Indexes. В этой секции у пользователя есть возможность увидеть информацию обо всех индексах для всех таблиц сразу (метрики собираются с представления pg_stat_user_indexes раз в час).

С помощью этой секции удобно оценивать использование и проблемы индексов по всему инстансу. Большие индексы, активно растущие, распухшие из-за блоатинга индексы, невалидные, неиспользуемые или редко используемые. Так же, как и в других табличных представлениях, тут удобно пользоваться сортировкой и фильтрацией для отбора топовых в том или ином разрезе индексов.
Заключение
В современных системах управления базами данных мониторинг играет критически важную роль в обеспечении стабильности, производительности и надежности работы. Рассматриваемые дашборды для визуализации собранных метрик представляют собой мощные инструменты, которые позволяют пользователям не только отслеживать текущее состояние системы, но и самостоятельно выявлять и решать проблемы с производительностью без постоянного обращения к администраторам базы данных. Внедренная нами система сбора метрик и созданные на их основе дашборды активно используются нашими командами на протяжении нескольких месяцев.
Основные выводы по прошествии этого времени:
Дашборды предоставляют пользователям доступ к ключевым метрикам, таким как использование ресурсов (CPU, память, диск), активность запросов, состояние репликаций, прогресс очистки и сбора статистики и т. д. Это позволяет им принимать осознанные решения по оптимизации своих рабочих нагрузок и предотвращению потенциальных проблем.
Визуализация данных помогает своевременно выявлять тренды и аномалии, такие как рост лагов репликации, увеличение времени выполнения запросов или накопление мертвых кортежей. Такой подход позволяет реагировать на проблемы до того, как они станут критическими.
Метрики, связанные с pg_stat_activity, pg_stat_statements, pg_stat_user_tables, pg_stat_progress_vacuum и другими системными представлениями, могут быть сложными для понимания в сыром виде. Дашборды преобразуют эти данные в наглядные графики, диаграммы и сводные таблицы, делая их доступными даже для менее технически подготовленных пользователей.
Когда пользователи могут самостоятельно анализировать производительность и решать типичные проблемы, это снижает нагрузку на администраторов базы данных. Администраторы могут сосредоточиться на более сложных задачах, таких как планирование масштабирования, оптимизация архитектуры и внедрение новых технологий.
Предоставление пользователям инструментов для самостоятельного мониторинга и анализа способствует развитию культуры ответственности за производительность приложений. Это приводит к более тесному взаимодействию между разработчиками, DevOps-инженерами и администраторами баз данных, что в конечном итоге улучшает общую эффективность работы всей организации.
Куда собираемся двигаться дальше? В этом году мы решили сосредоточится на использовании ИИ для анализа всей собираемой информации. В самом деле, у нас огромное количество метрик, мы знаем, когда БД работает нормально, а когда требуется вмешательство. Ну и еще мы знаем, когда произошел сбой ))
Выглядит заманчивым и логичным воспользоваться возможностями нейронных сетей и попробовать обучить модель находить проблемные инстансы, определять более подходящие под профиль нагрузки параметры, давать рекомендации по запросам, таблицам и индексам, ну и предсказывать будущие сбои наконец. И у нас появится еще один эффективный помощник — ну, или мы станем его помощниками ))