Дэйв Пэйдж. Эффективный мониторинг PostgreSQL: Использование представлений pg_stat_all_tables и pg_stat_all_indexes в PostgreSQL 16
Это перевод статьи Dave Page. Effective PostgreSQL Monitoring: Utilizing the views pg_stat_all_tables and pg_stat_all_indexes in PostgreSQL 16
Содержание
- Содержание
- Введение
- Понимание представлений pg_stat_all_tables и pg_stat_all_indexes
- Вклад EDB в PostgreSQL 16
- Мониторинг использования индексов
- Мониторинг последовательных сканирований:
- Эффективный мониторинг использования индексов и последовательных сканирований
- Заключение
- Использованные материалы
Введение
Мониторинг и уведомления о различных аспектах базы данных PostgreSQL имеет ключевое значение для обеспечения оптимальной производительности и поиска узких мест. В этом блоге мы рассмотрим представления pg_stat_all_tables
и pg_stat_all_indexes
, которые используются для отслеживания использования индексов и статистики таблиц. Путём оперативного отслеживания этих показателей и принятия соответствующих шагов можно найти области оптимизации и поднять общую производительность базы данных PostgreSQL.
Понимание представлений pg_stat_all_tables и pg_stat_all_indexes
Системные представления PostgreSQL pg_stat_all_tables
и pg_stat_all_indexes
позволяют находить способы повышения производительности и информацию об использовании таблиц и индексов.
- pg_stat_all_tables: В этом представлении можно найти статистику обо всех таблицах в текущей базе данных, включая количество последовательных сканирований и сканированием по индексу, обновлений, удалений, вставок и т.п. В нём также есть информация о количестве актуальных и устаревших кортежей, статистика об очистке таблиц от устаревших записей и анализе таблиц.
- pg_stat_all_indexes: В этом представлении можно найти подробную статистику обо всех индексах в текущей базе данных. Оно содержит количество сканирований индекса, отметку времени последнего сканирования по индексу и количество строк, полученных с помощью индекса.
Вклад EDB в PostgreSQL 16
Я реализовал запись статистики о последнем последовательном санировании таблиц и сканировании по индексу.
- В представление
pg_stat_all_indexes
добавлена отметка времени последнего сканирования по индексу (last_idx_scan
) - В представление
pg_stat_all_tables
добавлена отметка времени последнего последовательного сканирования (last_seq_scan
)
https://www.postgresql.org/docs/16/release-16.html#RELEASE-16-MONITORING
Цель этих улучшений - легко определить редко используемые индексы и таблицы. Хотя idx_scan
и seq_scan
уже содержат количество выполненных сканирований, эти значения не позволяют узнать, когда это происходило. Время может оказаться важнее частоты.
Отметки времени в сочетании с количеством выполненных сканирований позволяют определить неиспользуемые таблицы и индексы. Их можно удалить, чтобы сэкономить место, поднять производительность запросов и уменьшить время резервного копирования.
Мониторинг использования индексов
Индексы играют ключевую роль в улучшении производительности запросов, позволяя ускорить извлечение данных. Мониторинг использования индексов помогает найти как редко используемые, так и важные часто используемые индексы. В представлении pg_stat_all_indexes
есть следующие столбцы:
Столбец | Тип | Описание | |
---|---|---|---|
1 | relid | oid | OID индексируемой таблицы |
2 | indexrelid | oid | OID индекса |
3 | schemaname | name | Название схемы, в которой находится индекс |
4 | relname | name | Название индексируемой таблицы |
5 | indexrelname | name | Название индекса |
6 | idx_scan | bigint | Количество сканирований этого индекса |
7 | last_idx_scan | timestamp with time zone | Время завершения последней транзакции, в которой использовался этот индекс |
8 | idx_tup_read | bigint | Количество элементов индекса, возвращённых при сканировании этого индекса |
9 | idx_tup_fetch | bigint | Количество актуальных строк таблицы, прочитанных при простом сканировании этого индекса |
Важно проверить следующие столбцы:
- Количество сканирований индекса (idx_scan): Столбец
idx_scan
в представленииpg_stat_all_indexes
содержит количество сканирований таблицы с использованием индекса. С его помощью можно определить, какие индексы используются часто, какие - редко, а какие не используются вовсе. - Время последнего сканирования индекса (last_idx_scan): Столбец
last_idx_scan
вpg_stat_all_indexes
- это нововведение PostgreSQL 16. В нём содержится время последнего сканирования индекса. Учтите, что отметка времени соответствует времени окончания транзакции, а не в точности времени использования индекса (которое может значительно отличаться в случае длительных транзакций). - idx_tup_read - это количество элементов индекса, возвращённых при сканировании этого индекса. Его значение увеличивается при каждом чтении индекса.
- idx_tup_fetch - это количество актуальных строк таблицы/кучи, полученных при сканировании индекса.
Продемонстрируем на следующем примере:
CREATE TABLE sales (
id integer NOT NULL,
email VARCHAR,
location VARCHAR,
item VARCHAR,
price VARCHAR
);
INSERT INTO sales (
id, email, location, item, price
)
VALUES
(
1, 'a@hotmail.com', 'London', 'item1', '$50'
),
(
2, 'b@hotmail.com', 'Australia', 'item2', '$5'
),
(
3, 'c@hotmail.com', 'Canada', 'item3', '$12'
),
(
4, 'd@hotmail.com', 'London', 'item4', '$2'
),
(
5, 'e@hotmail.com', 'Switzerland', 'item5', '$150'
),
(
6, 'f@hotmail.com', 'Canada', 'item6', '$49'
);
CREATE INDEX idx_sale_id ON sales(id);
Давайте проверим количество строк в представлении pg_stat_all_indexes
:
postgres=# SELECT * FROM pg_stat_all_indexes WHERE indexrelname='idx_sale_id';
-[ RECORD 1 ]-+------------------------------
relid | 24576
indexrelid | 24581
schemaname | public
relname | sales
indexrelname | idx_sale_id
idx_scan | 0
last_idx_scan |
idx_tup_read | 0
idx_tup_fetch | 0
Теперь мы перейдём к запросу для выборки из таблицы sales, но перед этим отключим параметр конфигурации seqscan
, чтобы вынудить планировщик использовать сканирование по индексу.
postgres=# SET enable_seqscan=off;
SET
postgres=# EXPLAIN ANALYZE SELECT * FROM sales WHERE id = 5;
QUERY PLAN
-----------------------------------------------------------------------------------------
Index Scan using idx_sale_id_2 on sales (cost=0.13..8.15 rows=1 width=132) (actual time=0.057..0.059 rows=1 loops=1)
Index Cond: (id = 5)
Planning Time: 0.076 ms
Execution Time: 0.077 ms
(4 rows)
Теперь можно увидеть, что представление pg_stat_all_indexes
обновилось и можно увидеть отметку времени последнего сканирования индекса:
postgres=# SELECT * FROM pg_stat_all_indexes WHERE indexrelname='idx_sale_id';
-[ RECORD 1 ]-+------------------------------
relid | 24576
indexrelid | 24581
schemaname | public
relname | sales
indexrelname | idx_sale_id
idx_scan | 1
last_idx_scan | 2023-07-13 07:15:49.886831-04
idx_tup_read | 1
idx_tup_fetch | 1
Чтобы найти неиспользуемые/редкоиспользуемые индексы, проверьте приведённый ниже запрос:
SELECT
idxstat.schemaname as schema_name,
idxstat.relname AS table_name,
indexrelname AS index_name,
idxstat.idx_scan AS index_scans_count,
idxstat.last_idx_scan AS last_idx_scan_timestamp,
pg_size_pretty(pg_relation_size(idxstat.indexrelid)) AS index_size
FROM
pg_stat_all_indexes AS idxstat
JOIN
pg_index i ON idxstat.indexrelid = i.indexrelid
WHERE
idxstat.schemaname NOT IN ('pg_catalog', 'information_schema', 'pg_toast')
AND NOT i.indisunique -- is not a UNIQUE index
ORDER BY
idxstat.idx_scan ASC,
idxstat.last_idx_scan ASC;
Этот запрос отображает список всех индексов, отсортированный по частоте сканирования (столбец 4) и времени последнего сканирования (столбец 5). Если частота сканирования некоторых индексов (index_scans_count) равна нулю, меньше 10 или значение last_idx_scan_timestamp очень старое, возможно будет разумным удалить их.
Причины избавиться от неиспользуемых индексов:
- Зачастую индексы баз данных занимают такой же или даже больший объём, чем сами данные. Кроме этого хранилище необходимой скорости для базы данных может быть довольно дорогим. Также индексы увеличивают размер физической резервной копии и длительность такого резервного копирования.
- Индексы замедляют операции DML, изменяющие данные. При каждом запуске команды
INSERT
илиDELETE
в таблице все индексы нужно изменить в соответствии с таблицей ("куча"). Обновление индексированной таблицы обычно обходится дороже, чем обновление неиндексированной. - Обновления HOT не применяются при наличии индекса. Каждая операция
UPDATE
приводит к записи новой версии строки (также известной как "кортеж"), что приводит к созданию нового элемента в каждом индексе таблицы и не позволяет обновить данные на месте.
Кандидата на удаление нужно проверить следующим образом:
- Возможно планировщик избегает использовать индекс из-за дублирования или некорректной настройки таких параметров PostgreSQL, как effective_cache_size и random_page_cost, или может считать более выгодным последовательное сканирование таблицы по другим причинам. Решение нужно принимать, учитывая другие причины в пользу наличие (или отсутствие) других причин для его удаления.
- Сколько места занимает индекс? Возможно сейчас не стоит обращать на него внимание?
- Сколько раз происходило последовательное сканирование таблицы? Для предотвращения последовательных сканирований может потребоваться добавить больше столбцов в индекс.
- Может быть не нужно удалять таблицу или индекс из-за их неактивности. Может быть это таблица журнала аудита, которая используется редко, но нужна для согласованности, а индекс может использоваться только при итоговой обработке данных в конце года.
Мониторинг последовательных сканирований:
Последовательное сканирование означает, что PostgreSQL читает данные из таблицы последовательно без использования индекса. Если дело касается больших таблиц, последовательное сканирование неэффективно и может занять много времени на чтение всей таблицы. Если PostgreSQL выполняет одновременно несколько последовательных сканирований, падение производительности может быть чрезвычайно большим.
Мониторинг последовательное сканирования может способствовать обнаружению проблем производительности и подсказать возможные изменения индексов. Представление pg_stat_all_tables
содержит перечисленные ниже столбцы:
Столбец | Тип | Описание | |
---|---|---|---|
1 | relid | oid | OID таблицы |
2 | schemaname | name | Название схемы, в которой находится таблица |
3 | relname | name | Название таблицы |
4 | seq_scan | bigint | Количество последовательных сканирований этой таблицы |
5 | last_seq_scan | timestamp with time zone | Время завершения последней транзакции, в ходе которой произошло последовательное сканирование этой таблицы |
6 | seq_tup_read | bigint | Количество актуальных строк, прочитанных последовательным сканированием |
7 | idx_scan | bigint | Количество сканирований индекса в этой таблице |
8 | last_idx_scan | timestamp with time zone | Время завершения последней транзакции, в ходе которой произошло сканирование индекса этой таблицы |
9 | idx_tup_fetch | bigint | Количество актуальных строк, полученных индексом |
10 | n_tup_ins | bigint | Общее количество вставленных строк |
11 | n_tup_upd | bigint | Общее количество обновлённых строк (включает в себя количество обновлений строк из n_tup_hot_upd и n_tup_newpage_upd , и остальных не-HOT обновлений) |
12 | n_tup_del | bigint | Общее количество удалённых строк |
13 | n_tup_hot_upd | bigint | Количество HOT-обновлений строк. Эти обновления не требуют обновления связанных индексов |
14 | n_tup_newpage_upd | bigint | Количество обновлённых строк, попавших в новую страницу кучи, когда у исходной версии строки поле t_ctid указывает на другую страницу кучи. Это всегда не-HOT обновления |
15 | n_live_tup | bigint | Оценочное количество актуальных строк |
16 | n_dead_tup | bigint | Оценочное количество неактуальных строк |
17 | n_mod_since_analyze | bigint | Оценочное количество строк, изменённых в таблице с момента последнего анализа таблицы |
18 | n_ins_since_vacuum | bigint | Оценочное количество строк, вставленных в таблицу с последней очистки таблицы от неактуальных строк |
19 | last_vacuum | timestamp with time zone | Время последней ручной очистки таблицы от неактуальных строк (не считая VACUUM FULL ) |
20 | last_autovacuum | timestamp with time zone | Время последней автоматической очистки таблицы |
21 | last_analyze | timestamp with time zone | Время последнего ручного анализа таблицы |
22 | last_autoanalyze | timestamp with time zone | Время последнего автоматического анализа таблицы |
23 | vacuum_count | bigint | Сколько раз таблица была очищена от неактуальных строк вручную (не считая VACUUM FULL ) |
24 | autovacuum_count | bigint | Сколько раз таблица была очищена от неактуальных строк автоматически |
25 | analyze_count | bigint | Сколько раз таблица была проанализирована вручную |
26 | autoanalyze_count | bigint | Сколько раз таблица была проанализирована автоматически |
Важно проверить следующие столбцы:
- Количество последовательных сканирований (seq_scan): Столбец
seq_scan
в представленииpg_stat_all_tables
содержит общее количество последовательных сканирований определённой таблицы. Большое количество последовательных сканирований указывает, что подходящий индекс отсутствует или используется не часто. - Время последнего последовательного сканирования (last_seq_scan): Столбец last_seq_scan в представлении
pg_stat_all_tables
- нововведение PostgreSQL 16. В нём содержится время последнего последовательного сканирования таблицы. Этот столбец обновляется значением времени завершения транзакции (GetCurrentTransactionStopTimestamp()). - Время последнего сканирования индекса (last_idx_scan): Столбец
last_idx_scan
в представленииpg_stat_all_tables
совпадает по смыслу с одноимённым столбцом в представленииpg_stat_all_indexes
, но не содержит информации о том, какой индекс использовался при последнем сканировании. - Фрагментированные строки (n_dead_tup): Показывает примерное количество неактуальных строк, данные которых были удалены и которые можно использовать повторно при выполнении команд DELETE или UPDATE.
- Количество HOT-обновлений (n_tup_hot_upd): Можно посчитать коэффициент HOT-обновлений для наиболее обновляемых таблиц с помощью данных из этих столбцов. Столбцы n_tup_upd и n_tup_hot_upd в этом представлении содержат количество обычных и HOT-обновлений для определённой таблицы. Таблицы с высоким коэффициентом HOT-обновлений - "хорошие". Стоит обращать внимания на интенсивно обновляемые таблицы с низким или нулевым коэффициентом HOT-обновлений. Обычно у таких таблиц стоит настроить коэффициент заполнения fillfactor, чтобы зарезервировать больше места для добавления новых строк или увеличения таблицы. Наличие зарезервированного места позволяет повысить вероятность обновления строки внутри страницы с помощью HOT-обновления. Подробности о HOT-обновлениях можно найти по ссылке: https://www.postgresql.org/docs/16/storage-hot.html
Есть и другие полезные столбцы, такие как время последней ручной и автоматической очистки от неактуальных строк (last_vacuum и last_autovacuum), время последнего ручного анализа и автоматического анализа (last_analyze и last_autoanalyze) и их количество.
Чтобы найти в PostgreSQL старые таблицы, можно воспользоваться следующим запросом:
SELECT
tabstat.schemaname AS schema_name,
tabstat.relname AS table_name,
tabstat.seq_scan AS tab_seq_scan_count,
tabstat.idx_scan AS tab_index_scan_count,
tabstat.last_seq_scan AS tab_last_seq_scan_timestamp,
tabstat.last_idx_scan AS tab_last_idx_scan_timestamp,
pg_size_pretty(pg_total_relation_size(tabstat.relid)) AS table_size
FROM
pg_stat_all_tables AS tabstat
WHERE
tabstat.schemaname not in ('pg_catalog', 'information_schema', 'pg_toast')
ORDER BY
tabstat.last_seq_scan ASC,
tabstat.last_idx_scan ASC;
Этот запрос показывает список всех таблиц, отсортированный по времени последнего последовательного сканирования (5 столбец) и времени последнего сканирования по индексу (столбец 6). Выражение ORDER также можно заменить на столбцы seq_scan и idx_scan, чтобы получить список редко используемых таблиц.
Эффективный мониторинг использования индексов и последовательных сканирований
Для эффективного мониторинга использования индексов и последовательных сканирований стоит придерживаться следующих рекомендаций:
a. Регулярно анализируйте статистику
Регулярно анализируйте представления pg_stat_all_tables
и pg_stat_all_indexes
. Наблюдайте за тенденциями, аномалиями и возможными улучшениями.
b. Настройте автоматический мониторинг
Используйте скрипты или инструменты мониторинга для автоматизации сбора и анализа статистики. Они должны непрерывно сканировать базу данных и отправлять уведомления при выходе за пределы или при обнаружении тенденций.
c. Сравнивайте исторические данные
Сохраняйте статистику о последовательных сканированиях и использованиях индексов для сравнения и поиска повторяющихся шаблонов. Так можно обнаружить тенденции и оценить влияние изменений на базу данных.
d. Оптимизируйте использование индекса
Используйте столбцы idx_scan
и last_idx_scan
в представлении pg_stat_all_indexes
для обнаружения редко используемых индексов. Чтобы снизить стоимость обслуживания и увеличить производительность запросов вставки и обновления данных подумайте о том, чтобы удалить или изменить эти индексы.
e. Оптимизируйте последовательные сканирования
С помощью столбцов seq_scan
и last_seq_scan
в представлении pg_stat_all_tables
найдите таблицы с большим количеством последовательных сканирований. Оцените необходимость в дополнительных индексах. Также можно удалить или заархивировать таблицы, которые не использовались по данным мониторинга столбцов last_seq_scan
и last_idx_scan
. Так можно снизить использование места на диске и, как следствие, ускорить резервное копирование.
f. Анализируйте производительность запросов
Анализируйте долгие запросы с помощью команд PostgreSQL EXPLAIN и EXPLAIN ANALYZE. Обратите внимание на планы выполнения, использование индексов и последовательных сканирований, указанные в выводе. Обнаружив запросы с низким использованием индексов или использующие дорогое последовательное сканирование, можно найти области, требующие оптимизации.
Заключение
Для достижения максимальной производительности базы данных PostgreSQL важен мониторинг использования индексов и последовательных сканирований. С помощью представлений pg_stat_all_tables и pg_stat_all_indexes можно много узнать о таблице и использовании индексов. Обнаружив редко используемые индексы, отслеживая соотношение сканирований по индексу и последовательных сканирований, можно оптимизировать производительность запросов. Так можно убедиться, что база данных PostgreSQL работает эффективно и предоставляет наилучшую производительность для приложения. Чтобы база данных продолжала эффективно работать, постоянно отслеживайте и настраивайте индексы в соответствии с результатами мониторинга. Удачных наблюдений!