- •Предисловие
- •Об этой книге
- •Глава 1. Обзор статистики
- •Внутреннее устройство PostgreSQL
- •Установка соединений и работа сеансов
- •Запросы как базовая единица рабочей нагрузки
- •Планирование и выполнение запросов
- •Ввод-вывод при выполнении запросов
- •Журнал сообщений СУБД
- •Репликация изменений
- •Архивирование журнала предзаписи
- •Фоновая синхронизация данных
- •Автоочистка
- •Интерфейс статистики
- •Статистика как отправная точка инструментов мониторинга
- •Особенности статистики
- •Тестовое окружение
- •Глава 2. Статистика активности
- •Ключ к пониманию происходящего в СУБД
- •Взаимодействие клиента и сервера
- •Источники информации об активности
- •Представление pg_stat_activity
- •Представление pg_locks
- •Особенности pg_stat_activity и pg_locks
- •Представление pg_stat_database
- •Подключенные клиенты
- •Отслеживание клиентских сеансов
- •Транзакционная активность
- •Статусы завершения сеансов
- •Состояния сеансов
- •Отслеживание состояний
- •Ожидания и блокировки
- •Отслеживание состояний с учетом ожиданий
- •Взаимоблокировки
- •Бездействующие транзакции
- •Время выполнения запросов и транзакций
- •Отслеживание времени ожидания блокировок
- •Использование pg_locks.waitstart
- •Использование pg_stat_activity.state_change
- •Дерево блокировок
- •Глава 3. Выполнение запросов и функций
- •Зачем нужен мониторинг запросов
- •Расширение pg_stat_statements
- •Метаданные запроса
- •Планирование запроса
- •Исполнение запроса
- •Сквозная идентификация с queryid
- •Построение отчетов на основе pg_stat_statements
- •Представление pg_stat_statements_info
- •Выполнение процедур и функций
- •Глава 4. Базы данных
- •Иерархия объектов СУБД
- •Кластер баз данных
- •Табличные пространства
- •Базы данных
- •Схемы
- •Таблицы и индексы
- •TOAST
- •События в кластере баз данных
- •Рабочая нагрузка в отношении таблиц и индексов
- •Ошибки и нежелательные события
- •Функции для работы с объектами СУБД
- •Определение размеров объектов СУБД
- •Размещение объектов в файловой системе
- •Глава 5. Область общей памяти и ввод-вывод
- •Анализ общей памяти
- •Представление pg_buffercache
- •Представление pg_shmem_allocations
- •Анализ памяти клиентских процессов
- •Оценка использования SLRU-кешей
- •Ввод-вывод в контексте объектов СУБД
- •Базы данных
- •Ввод-вывод в контексте выполнения запросов
- •Временные файлы
- •Уровень баз данных
- •Ввод-вывод при выполнении запросов
- •Отслеживание в журнале сообщений
- •Отслеживание активных временных файлов
- •Ввод-вывод фоновых процессов
- •Глава 6. Журнал упреждающей записи
- •Отслеживание активности в журнале
- •Представление pg_stat_wal
- •Представление pg_stat_statements
- •Архивирование журнала
- •Представление pg_stat_archiver
- •Очередь архивирования
- •Глава 7. Репликация
- •Обзор репликации
- •Инструменты отслеживания репликации
- •Представление pg_stat_replication
- •Представление pg_stat_wal_receiver
- •Cлоты репликации и pg_replication_slots
- •Публикации и подписки
- •Конфликты восстановления
- •Глава 8. Очистка
- •Введение в очистку
- •Особенности очистки на практике
- •Когда выполняется автоочистка?
- •Статистика выполнения очистки
- •Счетчик транзакций и предотвращение ошибок, связанных с его зацикливанием
- •Раздувание таблиц и индексов
- •Отслеживание активных процессов очистки
- •Представление pg_stat_activity
- •Представление pg_stat_progress_vacuum
- •Глава 9. Ход выполнения операций
- •Представление pg_stat_progress_analyze
- •Представление pg_stat_progress_basebackup
- •Представление pg_stat_progress_cluster
- •Представление pg_stat_progress_create_index
- •Представление pg_stat_progress_copy
- •Предметный указатель
226 |
Глава 9. Ход выполнения операций |
|
|
# SELECT |
|
|
a.pid, |
|
|
host(a.client_addr) AS started_from, |
|
|
to_char(backend_start, 'YYYY-MM-DD HH24:MI:SS') AS started_at, |
|
|
now() - backend_start AS duration, |
|
|
a.state, a.wait_event_type ||'.'|| a.wait_event AS waiting, |
|
|
p.phase, |
|
|
pg_size_pretty(p.backup_total) AS size_total, |
|
|
pg_size_pretty(p.backup_streamed) AS sent, |
|
|
round(100 * p.backup_streamed / greatest(p.backup_total,1), 2) AS "sent,%", |
|
|
p.tablespaces_total ||'/'|| p.tablespaces_streamed AS "ts_total/streamed" |
|
|
FROM pg_stat_progress_basebackup p |
|
|
INNER JOIN pg_stat_activity a ON p.pid = a.pid |
|
|
ORDER BY now() - backend_start DESC; |
|
|
-[ RECORD 1 ]--------------+------------------------- |
|
|
pid |
| 545355 |
|
started_from |
| 127.0.0.1 |
|
started_at |
| 2023-03-06 04:43:41 |
|
duration |
| 00:00:36.700719 |
|
state |
| active |
|
waiting |
| IO.BaseBackupRead |
|
phase |
| streaming database files |
|
size_total |
| 713 MB |
|
sent |
| 623 MB |
|
sent,% |
| 87.00 |
|
ts_total/streamed |
| 1/0 |
Из приведенного фрагмента видно,что резервное копированиедлится чутьбольше 30 секунд, клиенту отправлена бóльшая часть содержимого БД, копирование выполнено на 87% и уже подходит к завершению. Из представления pg_stat_activity взяты поле state и маркер ожидания, который в большинстве случаев будет показывать ожидания ввода-вывода, поскольку процесс копирует файлы данных и не использует блокировки.
9.3. Представление pg_stat_progress_cluster
КомандыCLUSTER1 иVACUUMFULL2 используютсядляпересозданиятаблиц.Онипотребляютмного ресурсов и устанавливают исключительную блокировку, запрещающую доступ к обрабатываемым таблицам. При неаккуратном использовании такие команды могут легко заблокироватьработу приложений вплотьдо своего завершения,поэтому администраторутребуется инструмент,позволяющийотслеживатьходихвыполнения.ДляэтоговСУБДестьпредставление pg_stat_progress_cluster, где каждая строка содержит информацию об отдельной операции со следующим набором полей:
1 www.postgresql.org/docs/current/sql-cluster.html
2 www.postgresql.org/docs/current/sql-vacuum.html
9.3. Представление pg_stat_progress_cluster |
227 |
•pid—идентификатор процесса в операционной системе;
•datid,datname—идентификатор и имя базы данных,в которой выполняется операция;
•relid—идентификатор пересоздаваемой таблицы;
•command—выполняемая команда,CLUSTER или VACUUM FULL;
•phase—фаза операции:
—initializing—подготовка к сканированию таблицы;
—seq scanning heap—последовательное сканирование таблицы;
—index scanning heap—команда CLUSTER выполняет индексное сканирование таблицы;
—sorting tuples—команда CLUSTER выполняет сортировку строк;
—writing new heap—выполняется запись данных в новый файл таблицы;
—swapping relation files—выполняется подмена старых файлов новыми;
—rebuilding index—перестроение индексов;
—performing final cleanup—очистка и завершение работы;
•cluster_index_relid—идентификатор индекса для команды CLUSTER;
•heap_tuples_scanned—общее количество просканированных строк втаблице,изменяется только на стадиях последовательного или индексного сканирования и записи новой таблицы;
•heap_tuples_written—общее количество записанных строк в таблице,изменяется только на стадиях последовательного или индексного сканирования и записи новой таблицы;
•heap_blks_total — общее количество блоков в таблице на начало последовательного сканирования таблицы;
•heap_blks_scanned — общее количество просканированных блоков, изменяется только на этапе последовательного сканирования таблицы;
•index_rebuild_count — общее количество перестроенных индексов, изменяется только на этапе перестроения индексов.
На основе перечисленных полей легко понять, на каком этапе находится выполнение команды,и оценить ход выполнения.Для большей информативноститакже имеет смысл соединить представление с pg_stat_activity и провести некоторую обработку.
Для получения осмысленного результата запроса в соседнем терминале нужно запустить команду CLUSTER или VACUUM FULL:
# VACUUM FULL pgbench_accounts;
Тогда в сеансе с запросом к pg_stat_progress_cluster можно будет увидеть следующий вывод:
228 |
Глава 9. Ход выполнения операций |
||
|
# SELECT |
|
|
|
a.pid, |
|
|
|
now() - a.xact_start AS xact_age, |
||
|
p.datname, |
|
|
|
p.relid::regclass AS relation, |
||
|
p.cluster_index_relid::regclass AS index, |
||
|
a.state, |
|
|
|
(SELECT count(distinct l.pid) FROM pg_locks l |
||
|
WHERE l.relation = p.relid AND NOT l.granted) AS blocked_total, |
||
|
a.wait_event_type ||'.'|| a.wait_event AS wait_event, |
||
|
p.phase, |
|
|
|
pg_size_pretty(p.heap_blks_total * ( |
||
|
SELECT current_setting('block_size')::int)) AS size_total, |
||
|
round(100 * p.heap_blks_scanned / |
||
|
greatest(p.heap_blks_total,1), 2) AS "scanned,%", |
||
|
coalesce(p.heap_tuples_scanned, 0) AS tuples_scanned, |
||
|
coalesce(p.heap_tuples_written, 0) AS tuples_written, |
||
|
a.query |
|
|
|
FROM pg_stat_progress_cluster p |
||
|
INNER JOIN pg_stat_activity a ON p.pid = a.pid |
||
|
ORDER BY now() |
- a.xact_start DESC; |
|
|
-[ RECORD 1 ]--+------------------------------- |
||
|
pid |
| |
550142 |
|
xact_age |
| |
00:00:06.867676 |
|
datname |
| |
pgbench |
|
relation |
| |
pgbench_accounts |
|
index |
| |
- |
|
state |
| |
active |
|
blocked_total |
| |
33 |
|
wait_event |
| |
IO.DataFileExtend |
|
phase |
| |
seq scanning heap |
|
size_total |
| |
257 MB |
|
scanned,% |
| |
70.00 |
|
tuples_scanned | |
1404122 |
|
|
tuples_written | |
1404122 |
|
|
query |
| |
VACUUM FULL pgbench_accounts; |
В приведенном примере команда VACUUMFULLдлится уже шестьсекунд,она выполнена на 70%, однакоеезавершенияждутеще33сеанса,вкоторыхвыполняютсязапросыкперестраиваемой таблице. Соответственно, приложения, которые выполняют эти запросы, также заблокированы и вынуждены ждать завершения команды.
9.4. Представление pg_stat_progress_create_index
При активной разработке приложений и при появлении в приложении новых типов запросов создание индексов может бытьдовольно частой операцией.Создание индексов,особенно для
9.4. Представление pg_stat_progress_create_index |
229 |
больших таблиц, может занимать продолжительное время,требовать значительных ресурсов и влиять на производительность конкурентных запросов.
Для отслеживания используется представление pg_stat_progress_create_index, где каждая строка описывает отдельный процесс,в котором выполняется построение индекса:
•pid—идентификатор процесса в операционной системе;
•datid,datname—идентификатор и имя базы данных,в которой происходит операция;
•relid—идентификатор таблицы,которой принадлежит индекс;
•index_relid—идентификатор индекса,над которым выполняется операция;
•command — выполняемая команда: CREATE INDEX, CREATE INDEX CONCURRENTLY, REINDEX или REINDEX CONCURRENTLY;
•phase—фаза операции:
—initializing—подготовка к работе;
—waiting for writers before build — команды CREATE INDEX CONCURRENTLY или REINDEX CONCURRENTLY ожидают завершения транзакций, которые удерживают блокировки назаписьимогутчитатьтаблицу.Фазапропускаетсяпривыполненииоперациивблокирующем режиме. Детали выполнения фазы можно отслеживать в lockers_total, lockers_done и current_locker_pid;
—building index — методы доступа, поддерживающие отслеживание, передают статистику о своем состоянии. Детали построения индекса можно отслеживать в по-
лях blocks_total и blocks_done, но также могут меняться и значения tuples_total и tuples_done;
—waiting for writers before validation — команды CREATE INDEX CONCURRENTLY или
REINDEX CONCURRENTLY ожидают завершения транзакций, которые удерживают блокировки на запись и могут записывать в таблицу. Эта фаза пропускается при выполнении операции в блокирующем режиме.Детали выполнения фазы можно отслеживать в lockers_total,lockers_done и current_locker_pid;
—index validation: scanning index — команда CREATE INDEX CONCURRENTLY сканирует ин-
декс на предмет строк, требующих проверки. Фаза пропускается при выполнении операции в блокирующем режиме. Детали выполнения фазы отражаются в полях blocks_total и blocks_done;
—index validation: sorting tuples—команда CREATE INDEX CONCURRENTLY сортирует стро-
ки,найденные в фазе сканирования;
—index validation: scanning table — команда CREATE INDEX CONCURRENTLY сканирует таб-
лицу, чтобы проверить строки индекса, собранные в предыдущих двух фазах. Фаза пропускается при выполнении операции в блокирующем режиме. Детали выполне-
ние фазы отражаются в blocks_total и blocks_done;
230Глава 9. Ход выполнения операций
—waiting for old snapshots — команды CREATE INDEX CONCURRENTLY или REINDEX CONCURRENTLY ожидают освобождения снимков теми транзакциями, которые могут видеть содержимое таблицы. Фаза пропускается при выполнении операции в блокирующем режиме. Детали выполнения отражаются в lockers_total, lockers_done
иcurrent_locker_pid;
—waiting for readers before marking dead — перед тем как пометить старый индекс как нерабочий, команда REINDEX CONCURRENTLY ожидает завершения транзакций, удерживающих блокировки чтения. Фаза пропускается при выполнении операции в блокирующем режиме. Детали выполнения отражаются в lockers_total, lockers_done
иcurrent_locker_pid;
—waiting for readers before dropping — прежде чем удалить старый индекс, команда REINDEX CONCURRENTLY ожидает завершения транзакций, которые удерживают блокировки чтения.Фаза пропускается при выполнении операции в блокирующем режиме.
Детали выполнения отражаются в lockers_total, lockers_done и current_locker_pid;
•lockers_total—количество процессов,которых приходится ждать;
•lockers_done—количество процессов,ожидание которых завершено;
•current_locker_pid — идентификатор процесса, который удерживает блокировку в данный момент;
•blocks_total (tuples_total) — общее количество блоков (строк), которое требуется обработать в данной фазе;
•blocks_done (tuples_done)—общее количество блоков (строк),уже обработанных вданной фазе;
•partitions_total — общее число секций, для которых должны быть созданы индексы (в случае работы с секционированной таблицей);
•partitions_done — общее число секций, для которых уже выполнено создание индексов (в случае работы с секционированной таблицей).
Представлениедовольно подробно показываетход построения индекса.Особенно стоитотметить,чтозначения,указывающиенаблокиистроки,относятсякотдельнымфазам,анековсему процессу целиком.
Для получения результатов запроса в соседнем сеансе нужно запуститьперестроение индекса:
# REINDEX CONCURRENTLY pgbench_accounts_pkey;
В сеансе с запросом к pg_stat_progress_create_index можно будет увидеть подобный вывод, из которого следует, что перестроение индекса находится в фазе сканирования таблицы, которая завершена на 75%. После этого будет начата фаза загрузки строк, ход которой можно будет отследить в поле tuples_done,%.
