- •Предисловие
- •Об этой книге
- •Глава 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
- •Предметный указатель
130Глава 5. Область общей памяти и ввод-вывод
5.1. Анализ общей памяти
Область общей памяти используется для оперативного размещения данных таблиц,индексов и большого количества различных служебных структур. Общей она называется потому, что все процессы СУБД имеют к ней равный доступ и изменения, внесенные одним процессом, становятся доступными всем остальным. Как правило, самой большой частью общей памяти является буферный, или, как его еще называют, общий кеш, который используется для размещения пользовательских данных.
Сбуферным кешем почти никогда не возникает проблем ровно до тех пор, пока он способен вместить себя все данные из основного хранилища (основной каталог данных и табличные пространства). Однако, когда объем хранимых данных превышает объем общего кеша, появляется задача эффективного использования отведенного объема: как держать в нем нужные данные и замещать ненужные в случае нехватки места. Могут возникнуть вопросы: какие системные структуры находятся в общей памяти и сколько они занимают места? Какие объекты (таблицы, индексы) находятся в буферном кеше? Каковы размеры и доля этих объектов относительно друг друга? Насколько эффективно используется буферный кеш? Какой процент обращений заканчивается успехом? Как часто данные не удается найти и приходится обращаться к основному хранилищу? Для ответа на эти вопросы СУБД предлагает несколько инструментов, которые можно использовать как для разового анализа, так и для регулярного мониторинга:
•pg_buffercache1 —отдельное расширение с представлением (на основе функции),которое показывает использование буферов общего кеша;
•pg_shmem_allocations2 —представлениесинформациейотом,какиеслужебныеструктуры находятся в общей памяти.
Спомощью этих представлений можно получить информацию о содержимом общей памяти с точки зрения как таблиц и индексов, так и внутренних служебных структур. Давайте более подробно рассмотрим оба инструмента и сценарии их использования.
Представление pg_buffercache
Расширение pg_buffercache предоставляет возможность анализа буферного кеша с точки зрения пользовательских объектов и помогает получить ответы на такие вопросы,как:
•страницы каких таблиц и индексов находятся в общем кеше;
•какое количество буферов используется для размещения таблиц и индексов, их процентное отношение относительно всего объема кеша;
•сколько свободных и занятых («чистых» или «грязных») буферов находятся сейчас в кеше.
1 postgrespro.ru/docs/postgresql/current/pgbuffercache
2 postgrespro.ru/docs/postgresql/current/view-pg-shmem-allocations
5.1. Анализ общей памяти |
131 |
Для использования расширения достаточно установить его с помощью команды CREATE EXTENSION. После установки появятся соответствующие функция и представление. В тестовом окружении расширение уже установлено и готово к использованию.
# SELECT * FROM pg_buffercache LIMIT 10;
bufferid | relfilenode | reltablespace | reldatabase | relforknumber | relblocknumber | isdirty | usagecount | pinn...
----------+-------------+---------------+-------------+---------------+----------------+---------+------------+-----...
1 |
| |
1262 |
| |
1664 |
| |
0 |
| |
0 |
| |
0 |
| f |
| |
5 |
| |
2 |
| |
1260 |
| |
1664 |
| |
0 |
| |
0 |
| |
0 |
| f |
| |
5 |
| |
3 |
| |
1259 |
| |
1663 |
| |
5 |
| |
0 |
| |
0 |
| f |
| |
5 |
| |
4 |
| |
1259 |
| |
1663 |
| |
5 |
| |
0 |
| |
1 |
| f |
| |
5 |
| |
5 |
| |
1259 |
| |
1663 |
| |
5 |
| |
0 |
| |
2 |
| f |
| |
5 |
| |
6 |
| |
1259 |
| |
1663 |
| |
5 |
| |
0 |
| |
3 |
| f |
| |
5 |
| |
7 |
| |
1249 |
| |
1663 |
| |
5 |
| |
0 |
| |
0 |
| f |
| |
5 |
| |
8 |
| |
1249 |
| |
1663 |
| |
5 |
| |
0 |
| |
1 |
| f |
| |
5 |
| |
9 |
| |
1249 |
| |
1663 |
| |
5 |
| |
0 |
| |
2 |
| f |
| |
5 |
| |
10 |
| |
1249 |
| |
1663 |
| |
5 |
| |
0 |
| |
3 |
| f |
| |
5 |
| |
Каждая строка описывает отдельный буфер; соответственно, чем больше размер буферного кеша, тем больше будет строк в представлении. На основе практического опыта отмечу, что не помню случаев, когда бы могла понадобиться информация по отдельно взятому буферу: обычно нужна статистика,сгруппированная по объектам,базам итаблицам.При составлении запросов к pg_buffercache важно учитывать, что расширение устанавливается в конкретную БД, но статистика описывает все буферы, в том числе и те, что ассоциированы с объектами вдругихБД.Дляпреобразованиячисловыхидентификатороввименатаблицидругихотношений можно выполнить соединение с pg_class,нотакое преобразование будет работатьтолько для объектов изданной БД.Втаких случаях рекомендуетсядобавитьусловие,чтобы выводить буферы только для текущей БД,а не всех вообще.
Представление содержит несколько полей:
•bufferid — идентификатор конкретного буфера. У свободных (неиспользуемых) буферов значения всех полей,кроме bufferid,будут отсутствовать (NULL);
•relfilenode— номер файла отношения (ссылается на pg_class.relfilenode); по этому номеру объект можно найти в основном каталоге данных;
•reltablespace — идентификатор табличного пространства отношения (ссылается на поле pg_tablespace.oid);
•reldatabase— идентификатор базы данных (ссылается на pg_database.oid). Общие объекты,принадлежащие системному каталогу,будут иметь нулевое значение;
•relforknumber — идентификатор, указывающий на слой отношения (main, visibility map, free space map,init);
•relblocknumber—номер блока внутри отношения;
132Глава 5. Область общей памяти и ввод-вывод
•isdirty—флаг,указывающий нато,что буфер является грязным и содержимое находящегося в нем блока отличается от содержимого блока в основном хранилище;
•usagecount—счетчикобращенийкбуферу,которыйиспользуетсяалгоритмомClockSweep для вытеснения неиспользуемых страниц из общего кеша1;
•pinning_backends—текущее количество закреплений буфера клиентскими процессами.
Закрепление буферов
Пока процесс читает страницу,буфер необходимо блокировать от попыток изменения другими процессами,нодолгая блокировка буфера негативно скажется на конкурентной работе.Благодаряправиламвидимостистрокблокировкабуферанужнатольконавремячтения оглавления страницы; однако по-прежнему важно, чтобы страница не была вытеснена из буфераисодержимоестраницынеизменилоськардинально.Чтобыограничитьспектрвозможных действий с буфером, не блокируя его, используется так называемое закрепление (pin) буфера, которое позволяетдругим процессам читать и изменять данные, но не позволяет вытеснять страницу из буфера или очищать ее.
Используячисловыеидентификаторы,можноточноопределитьобъекты(таблицыииндексы) вбуферномкеше,ихпринадлежностькконкретнымбазамданныхидажеспомощьюфункции pg_relation_filepath узнать их расположение в файловой системе.
На основе полей isdirty, usagecount и pinning_backends можно выделить набор состояний, в которых могут находиться буферы, и таким образом определить степень использования буферного кеша. После инициализации буферного кеша на старте СУБД бóльшая часть буферов не используется и не ассоциирована ни с одним из блоков (значение bufferid отсутствует). При выполнении запросов СУБД обращается к основному хранилищу и заполняет буферный кешданнымитаблицииндексов;буферыизсвободногосостоянияпереходятвзанятое.Вэтом состоянии буферы ассоциированы с конкретными блоками таблиц и индексов. Такое состояние определяется полями reltablespace,reldatabase и relfilenode,которые вместе указывают на ассоциированный с буфером объект.
Также можно принять во внимание значение usagecount,которое показываеттекущую интенсивность обращений к буферу. Этот счетчик увеличивается на единицу при каждом доступе (максимальное значение — 5) и уменьшается на единицу при каждом проходе алгоритма вытеснения. Блок вытесняется и заменяется другим блоком, если к моменту уменьшения счетчика его значение уже равно нулю. Таким образом, по значению usagecount (от 0 до 5) можно определить,насколько активно используются буферы.
Состояниезанятогобуфераможнодетализировать.Так,например,кбуферумогутобращаться клиентские процессы,читая или изменяяданные блока,на что указываетненулевое значение pinning_backends. Если содержимое буфера было изменено, он становится грязным, и эти изменениядолжныбытьперенесенывассоциированныйсбуферомблоквосновномхранилище. На грязное состояние указывает флаг isdirty.
1 www.interdb.jp/pg/pgsql08.html#_8.4.4.
5.1. Анализ общей памяти |
133 |
Таким образом, использование буферного кеша можно рассматривать как минимум в двух проекциях:
1.На верхнем уровне по свободным,используемым,закрепленным и грязным буферам.
2.На детальном уровне на основе счетчика обращений (usagecount), значения которого варьируются от 0 до 5.
При использовании pg_buffercacheв регулярном мониторинге стоитучитывать,что представлениепоказываеттолькоснимоксостояниябуфероввмоментопроса;состояниебуферовмежду опросами остается неизвестным. Это поведение аналогично тому, как работают pg_locks
и pg_stat_activity.
Влияние на производительность
До версии PostgreSQL 10 внутренний механизм pg_buffercache использовал блокировки для получения согласованного состояния всех буферов. Эти блокировки приводили к снижению производительности в системах с интенсивной рабочей нагрузкой. Начиная с версии 10 блокировки в менеджере буферов больше не устанавливаются. Поэтому обращения к pg_buffercache стали меньше влиять на обычную активность буферов, но набор результатов, полученный для всех буферов, в целом может оказаться несогласованным (что, как правило, некритично). Внутри каждого отдельного буфера согласованность информации по-прежнему гарантируется.
Давайте вернемся чуть назад, к SQL-запросу к pg_buffercache. Практически весь его вывод представлен числовыми идентификаторами; для получения более понятной информации потребуется соединить результат с дополнительными источниками информации и проделать некоторые преобразования.
# SELECT n.nspname, c.relname,
count(*) AS buffers, pg_size_pretty(count(*) * 8192) AS bytes
FROM pg_buffercache b JOIN pg_class c
ON b.relfilenode = pg_relation_filenode(c.oid) AND b.reldatabase IN (
0,
(SELECT oid FROM pg_database WHERE datname = current_database())
)
JOIN pg_namespace n ON n.oid = c.relnamespace GROUP BY n.nspname, c.relname
ORDER BY 3 DESC LIMIT 10;
134 |
Глава 5. Область общей памяти и ввод-вывод |
|
|
|
|
|||
|
nspname |
| |
relname |
| buffers | |
bytes |
|||
|
------------ |
+ |
-------------------------------- |
+ |
--------- |
+ |
--------- |
|
|
public |
| |
pgbench_accounts |
| |
9902 |
| |
77 MB |
|
|
public |
| |
pgbench_accounts_pkey |
| |
5234 |
| |
41 MB |
|
|
public |
| |
pgbench_tellers |
| |
290 |
| |
2320 kB |
|
|
public |
| |
pgbench_history |
| |
198 |
| |
1584 kB |
|
|
pg_catalog | |
pg_proc |
| |
99 |
| |
792 |
kB |
|
|
pg_catalog | |
pg_attribute |
| |
38 |
| |
304 |
kB |
|
|
pg_catalog | |
pg_class |
| |
15 |
| |
120 |
kB |
|
|
pg_catalog | |
pg_proc_proname_args_nsp_index | |
14 |
| |
112 |
kB |
||
|
pg_catalog | |
pg_operator |
| |
14 |
| |
112 |
kB |
|
|
pg_catalog | |
pg_statistic |
| |
13 |
| |
104 |
kB |
|
Полученный результат стал более понятен: вместо числовых идентификаторов объектов теперь выводятся имена, статистика сгруппирована по объектам, а блоки просуммированы и дополнительно выражены в байтах. Теперь видно, что бóльшую часть в буферном кеше (в тестовом окружении значение shared_buffers установлено в 128 МБ) занимают таблица pgbench_accounts и ее индекс.
Преобразование в байты
Втестовомокружениимнезаранееизвестенразмерблока,ивпримерахзапросовиспользуется константа 8192.Однако на практике размер блока можетбытьпереопределен на этапе компиляцииизисходныхкодов,ивтакихслучаяхпреобразованиебудетневерным.Вместо константы можно использовать более универсальное решение и получать актуальный размер блока из конфигурации СУБД вызовом функции current_setting('block_size').Такой способпредпочтителен,когданужновыразитьразмерблоковвбайтах,ноприэтомточный размер блока неизвестен.
Следующим запросом можно получить информацию об использовании буферного кеша и узнать соотношение объемов буферов,находящихся в разных состояниях.
# SELECT
pg_size_pretty(count(*) FILTER (
WHERE reldatabase IS NULL) * 8192) AS free, pg_size_pretty(count(*) FILTER (
WHERE pinning_backends = 0 AND isdirty = 'f') * 8192) AS clean, pg_size_pretty(count(*) FILTER (
WHERE pinning_backends > 0 AND isdirty = 'f') * 8192) AS "clean/pinned", pg_size_pretty(count(*) FILTER (
WHERE pinning_backends = 0 AND isdirty = 't') * 8192) AS dirty, pg_size_pretty(count(*) FILTER (
WHERE pinning_backends > 0 AND isdirty = 't') * 8192) AS "dirty/pinned" FROM pg_buffercache;
