Добавил:
ИВТ (советую зайти в "Несортированное")rnПИН МАГА Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Database 2024 / Books / Мониторинг PostgreSQL.pdf
Скачиваний:
26
Добавлен:
20.11.2024
Размер:
6.87 Mб
Скачать

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;

Соседние файлы в папке Books