Динамическая трассировка#
Примечание
Эта страница переведена при помощи нейросети GigaChat.
PostgreSQL предоставляет средства для поддержки динамического отслеживания сервера баз данных. Это позволяет внешней утилите вызываться в определенных точках кода и тем самым отслеживать выполнение.
В исходный код уже вставлено несколько зондов или точек трассировки. Эти зонды предназначены для использования разработчиками баз данных и администраторами. По умолчанию зонды не компилируются в PostgreSQL, пользователь должен явно указать сценарию настройки сделать зонды доступными.
В настоящее время поддерживается утилита DTrace, которая, на момент написания этой статьи, доступна в Solaris, macOS, FreeBSD, NetBSD и Oracle Linux. Проект SystemTap для Linux предоставляет эквивалент DTrace и также может быть использован. Теоретически возможно поддерживать другие динамические утилиты трассировки путем изменения определений макросов в src/include/utils/probes.h.
Компиляция для динамической трассировки#
По умолчанию зонды недоступны, поэтому нужно будет явно указать сценарию настройки сделать зонды доступными в PostgreSQL. Чтобы включить поддержку DTrace, укажите --enable-dtrace к конфигурации. См. раздел «Процедура установки» для получения дополнительной информации.
Встроенные точки трассировки#
В исходном коде предоставляется ряд стандартных зондов, как показано в таблице ниже. Таблица «Предопределенные типы, используемые в параметрах зондирования» показывает типы, используемые в зондах. Конечно, можно добавить больше зондов для улучшения наблюдаемости PostgreSQL.
Встроенные точки трассировки DTrace:
Имя |
Параметры |
Описание |
|---|---|---|
|
|
Зонд, который срабатывает при начале новой транзакции. arg0 - это идентификатор транзакции |
|
|
Зонд, который срабатывает, когда транзакция завершается успешно. arg0 - это идентификатор транзакции |
|
|
Зонд, который срабатывает, когда транзакция завершается неуспешно. arg0 - это идентификатор транзакции |
|
|
Зонд, который срабатывает при запуске обработки запроса. Аргумент 0 - это строка запроса |
|
|
Зонд, который срабатывает при завершении обработки запроса. Аргумент 0 - это строка запроса |
|
|
Зонд, который срабатывает при начале разбора запроса. Аргумент 0 - это строка запроса |
|
|
Зонд, который срабатывает при завершении разбора запроса. Аргумент 0 - это строка запроса |
|
|
Зонд, который срабатывает при начале перезаписи запроса. Аргумент 0 - это строка запроса |
|
|
Зонд, который срабатывает при завершении перезаписи запроса. Аргумент 0 - это строка запроса |
|
|
Зонд, который срабатывает при начале планирования запроса |
|
|
Зонд, который срабатывает при завершении планирования запроса |
|
|
Зонд, который срабатывает при запуске выполнения запроса |
|
|
Зонд, который срабатывает при завершении выполнения запроса |
|
|
Зонд, который срабатывает всякий раз, когда серверный процесс обновляет свой |
|
|
Зонд, который срабатывает при начале контрольной точки. Аргумент 0 содержит битовую маску, используемую для различения различных типов контрольных точек, таких как завершение работы, немедленное или принудительное |
|
|
Зонд, который срабатывает при завершении контрольной точки. (Следующие зонды срабатывают последовательно во время обработки контрольной точки.) Arg0 - это количество записанных буферов. Arg1 - общее количество буферов. Arg2, arg3 и arg4 содержат количество добавленных, удаленных и переработанных файлов WAL соответственно |
|
|
Зонд, который срабатывает, когда начинается часть CLOG контрольной точки. Arg0 истинно для нормальной контрольной точки, ложно для контрольной точки завершения работы |
|
|
Зонд, который срабатывает, когда завершена часть CLOG контрольной точки. Arg0 имеет то же значение, что и для |
|
|
Зонд, который срабатывает, когда запускается часть SUBTRANS контрольной точки. Arg0 истинно для нормальной контрольной точки, ложно для контрольной точки завершения работы |
|
|
Зонд, который срабатывает при завершении части SUBTRANS контрольной точки. arg0 имеет то же значение, что и для |
|
|
Зонд, который срабатывает, когда начинается часть MultiXact контрольной точки. arg0 истинно для нормальной контрольной точки, ложно для контрольной точки завершения работы |
|
|
Зонд, который срабатывает, когда завершена часть MultiXact контрольной точки. arg0 имеет то же значение, что и для |
|
|
Зонд, который срабатывает, когда начинается часть записи буфера контрольной точки. arg0 содержит битовые флаги, используемые для различения различных типов контрольных точек, таких как завершение работы, немедленное или принудительное |
|
|
Зонд, который срабатывает, когда начинаю записываться грязные буферы во время контрольной точки (после определения того, какие буферы должны быть записаны). Arg0 - общее количество буферов. Arg1 - это число, которые в настоящее время повреждены и должны быть написаны |
|
|
Зонд, который срабатывает после записи каждого буфера во время контрольной точки. Arg0 - идентификационный номер буфера |
|
|
Зонд, который срабатывает, когда все поврежденные буферы были записаны. Arg0 - общее количество буферов. Arg1 - количество буферов, фактически записанных процессом контрольной точки. Arg2 - количество буферов, которые ожидалось записать (arg1 из |
|
|
Зонд, который срабатывает после записи поврежденных буферов в ядро и перед началом отправки запросов fsync |
|
|
Зонд, который срабатывает при завершении синхронизации буферов с диском |
|
|
Зонд, который срабатывает, когда начинается двухфазная часть контрольной точки |
|
|
Зонд, который срабатывает, когда завершается двухфазная часть контрольной точки |
|
|
Зонд, который срабатывает при запуске чтения буфера. Аргументы 0 и 1 содержат номера вилки и блока страницы (но аргумент 1 будет равен -1, если это запрос расширения отношения). Аргументы 2, 3 и 4 содержат OID пространства таблиц, базы данных и отношения, идентифицирующие отношение. Аргумент 5 - это идентификатор бэкенда, который создал временное отношение для локального буфера или |
|
|
Зонд, который срабатывает при завершении чтения буфера. Аргументы 0 и 1 содержат номера вилки и блока страницы (если это запрос расширения отношения, то теперь аргумент 1 содержит номер блока нового добавленного блока). Аргументы 2, 3 и 4 содержат OID пространства таблиц, базы данных и отношения, идентифицирующие отношение. Аргумент 5 - это идентификатор бэкенда, который создал временное отношение для локального буфера или |
|
|
Зонд, который срабатывает перед выдачей любого запроса на запись для общего буфера. arg0 и arg1 содержат номера вилки и блока страницы. arg2, arg3 и arg4 содержат OID пространства таблиц, базы данных и отношения, идентифицирующие отношение |
|
|
Зонд, который срабатывает при завершении запроса на запись. (Обратите внимание, что это просто отражает время передачи данных ядру; обычно он еще не был записан на диск.) Аргументы такие же, как для |
|
|
Зонд, который срабатывает, когда серверный процесс начинает записывать поврежденный буфер. (Если это происходит часто, это означает, что shared_buffers слишком мал или параметры управления фоновым писателем нуждаются в корректировке.) Аргументы 0 и 1 содержат номера вилки и блока страницы. Аргументы 2, 3 и 4 содержат OID пространства таблиц, базы данных и отношения, идентифицирующие отношение |
|
|
Зонд, который срабатывает при завершении записи поврежденного буфера. Аргументы такие же, как для |
|
|
Зонд, который срабатывает, когда серверный процесс начинает записывать грязный буфер WAL, потому что больше нет места для буфера WAL. (Если это происходит часто, это означает, что wal_buffers слишком мал.) |
|
|
Зонд, который срабатывает при завершении записи грязного буфера WAL |
|
|
Зонд, который срабатывает при вставке записи WAL. Arg0 - диспетчер ресурсов (rmid) для записи. В arg1 содержатся флаги информации |
|
|
Зонд, который срабатывает при запросе переключения сегмента WAL |
|
|
Зонд, который срабатывает при начале чтения блока из отношения. Аргументы 0 и 1 содержат номера вилки и блока страницы. Аргументы 2, 3 и 4 содержат OID пространства таблиц, базы данных и отношения, идентифицирующие отношение. Аргумент 5 - это идентификатор бэкенда, который создал временное отношение для локального буфера или |
|
|
Зонд, который срабатывает при завершении чтения блока. Аргументы 0 и 1 содержат номера вилки и блока страницы. Аргументы 2, 3 и 4 содержат OID пространства таблиц, базы данных и отношения, идентифицирующие отношение. Аргумент 5 - это идентификатор бэкенда, который создал временное отношение для локального буфера или |
|
|
Зонд, который срабатывает при начале записи блока в отношение. Аргументы 0 и 1 содержат номера вилки и блока страницы. Аргументы 2, 3 и 4 содержат OID пространства таблиц, базы данных и отношения, идентифицирующие отношение. Аргумент 5 - это идентификатор бэкенда, который создал временное отношение для локального буфера или |
|
|
Зонд срабатывает при завершении записи блока. Аргументы 0 и 1 содержат вилку и номера блоков страницы. Аргументы 2, 3 и 4 содержат OID таблицы пространства, базы данных и отношения, идентифицирующие отношение. Аргумент 5 - это идентификатор бэкенда, который создал временное отношение для локального буфера или |
|
|
Зонд срабатывает, когда начинается операция сортировки. Аргумент 0 указывает кучу, индекс или сортировку данных. Аргумент 1 истинен для обеспечения уникальности значений. Аргумент 2 - это количество столбцов ключей. Аргумент 3 - это количество килобайт рабочей памяти, разрешенной для работы. Аргумент 4 истинен, если требуется произвольный доступ к результату сортировки. Аргумент 5 указывает последовательный режим, когда |
|
|
Зонд срабатывает, когда сортировка завершена. Аргумент 0 истинен для внешней сортировки, ложен для внутренней сортировки. Аргумент 1 - это количество дисковых блоков, используемых для внешней сортировки, или килобайты памяти, используемые для внутренней сортировки |
|
|
Зонд срабатывает, когда приобретается LWLock. Аргумент 0 - это траншея LWLock. Аргумент 1 - это запрашиваемый режим блокировки, либо исключительный, либо общий |
|
|
Зонд срабатывает при освобождении LWLock (но обратите внимание, что все освобожденные ожидающие еще не были разбужены). Arg0 - это траншея LWLock |
|
|
Зонд срабатывает, когда LWLock немедленно недоступен и серверный процесс начинает ожидать освобождения блокировки. Arg0 - это траншея LWLock. Arg1 - запрошенный режим блокировки, либо исключительный, либо общий |
|
|
Зонд срабатывает, когда серверный процесс был освобожден от ожидания LWLock (у него еще нет блокировки). Arg0 - это траншея LWLock. Arg1 - запрошенный режим блокировки, либо исключительный, либо общий |
|
|
Зонд срабатывает, когда LWLock успешно приобретается, когда вызывающий абонент указывает отсутствие ожидания. Arg0 - это траншея LWLock. Arg1 - запрошенный режим блокировки, либо исключительный, либо общий |
|
|
Зонд срабатывает, когда LWLock не удалось успешно получить, когда вызывающий абонент указал отсутствие ожидания. Arg0 - это траншея LWLock. Arg1 - запрошенный режим блокировки, либо исключительный, либо общий |
|
|
Зонд срабатывает, когда запрос тяжелой блокировки (блокировка lmgr) начинает ожидать, потому что блокировка недоступна. Аргументы от 0 до 3 являются полями тега, идентифицирующими заблокированный объект. Arg4 указывает тип объекта, который блокируется. Arg5 указывает тип блокировки, которая запрашивается |
|
|
Зонд срабатывает, когда запрос тяжелой блокировки (замок lmgr) закончил ожидание (т.е. получил замок). Аргументы те же, что и для |
|
|
Зонд срабатывает, когда детектор взаимоблокировки обнаруживает взаимоблокировку |
Предопределенные типы, используемые в параметрах зондирования:
Тип |
Определение |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Использование точек трассировки#
В приведенном ниже примере показан сценарий DTrace для анализа количества транзакций в системе в качестве альтернативы созданию моментальных снимков pg_stat_database до и после теста производительности:
#!/usr/sbin/dtrace -qs
postgresql$1:::transaction-start
{
@start["Start"] = count();
self->ts = timestamp;
}
postgresql$1:::transaction-abort
{
@abort["Abort"] = count();
}
postgresql$1:::transaction-commit
/self->ts/
{
@commit["Commit"] = count();
@time["Total time (ns)"] = sum(timestamp - self->ts);
self->ts=0;
}
При выполнении пример сценария D дает вывод, аналогичный следующему:
# ./txn_count.d `pgrep -n postgres` or ./txn_count.d <PID>
^C
Start 71
Commit 70
Total time (ns) 2312105013
Примечание
SystemTap использует другую нотацию для скриптов трассировки, чем DTrace, хотя основные точки трассировки совместимы. Одним из моментов, которые стоит отметить, является то, что на момент написания этой статьи сценарии SystemTap должны ссылаться на имена датчиков с использованием двойных подчеркиваний вместо дефисов. Ожидается, что это будет исправлено в будущих версиях SystemTap.
Следует помнить, что сценарии DTrace необходимо тщательно писать и отлаживать, иначе собранная информация о трассировке может оказаться бессмысленной. В большинстве случаев, когда возникают проблемы, виновато инструментальное средство, а не основная система. При обсуждении информации, полученной с помощью динамической трассировки, убедитесь, что сценарий, используемый для этого, также заключен в скобки, чтобы его тоже можно было проверить и обсудить.
Определение новых точек трассировки#
Новые зонды могут быть определены в коде везде, где разработчику это угодно, хотя для этого потребуется перекомпиляция. Ниже приведены шаги для вставки новых зондов:
Определите имена датчиков и данные, которые будут доступны через датчики
Добавьте определения датчиков в
src/backend/utils/probes.dВключите
pg_trace.h, если он еще не присутствует в модуле(ях), содержащем точки датчика, и вставьтеTRACE_POSTGRESQLмакросы датчика в желаемые места в исходном кодеПересоберите и убедитесь, что новые датчики доступны.
Пример добавление точки для трассировки всех новых транзакций по идентификатору транзакции:
Пробник будет называться
transaction-startи принимать параметр типаLocalTransactionId.Добавьте определение пробника к
src/backend/utils/probes.d:probe transaction__start(LocalTransactionId);Обратите внимание на использование двойного подчеркивания в имени пробника. В сценарии DTrace, использующем зонд, двойное подчеркивание необходимо заменить дефисом, поэтому
transaction-startявляется именем, которое следует задокументировать для пользователей.Во время компиляции
transaction__startпреобразуется в макроопределение под названиемTRACE_POSTGRESQL_TRANSACTION_START(обратите внимание, что здесь подчеркивание одинарное), которое доступно при включенииpg_trace.h. Добавьте вызов макроса в соответствующее место исходного кода. В данном случае это выглядит следующим образом:TRACE_POSTGRESQL_TRANSACTION_START(vxid.localTransactionId);После перекомпиляции и запуска нового двоичного файла проверьте, доступен ли недавно добавленный зонд, выполнив следующую команду DTrace. Вывод должен быть аналогичен:
# dtrace -ln transaction-start ID PROVIDER MODULE FUNCTION NAME 18705 postgresql49878 postgres StartTransactionCommand transaction-start 18755 postgresql49877 postgres StartTransactionCommand transaction-start 18805 postgresql49876 postgres StartTransactionCommand transaction-start 18855 postgresql49875 postgres StartTransactionCommand transaction-start 18986 postgresql49873 postgres StartTransactionCommand transaction-start
Есть несколько моментов, о которых следует помнить при добавлении макросов трассировки к коду на языке Си:
Нужно убедиться, что типы данных, указанные для параметров зонда, соответствуют типам данных переменных, используемых в макросе. В противном случае будут получены ошибки компиляции.
На большинстве платформ, если PostgreSQL построен с
--enable-dtrace, аргументы трассируемого макроса будут оцениваться каждый раз при прохождении через этот макрос, даже если нет никакого отслеживания. Обычно об этом не стоит беспокоиться, если просто сообщаете значения нескольких локальных переменных. Но остерегайтесь помещать дорогостоящие вызовы функций в аргументы. Если нужно сделать это, рассмотрите возможность защиты макроса проверкой, чтобы увидеть, действительно ли включен трассировщик:if (TRACE_POSTGRESQL_TRANSACTION_START_ENABLED()) TRACE_POSTGRESQL_TRANSACTION_START(some_function(...));Каждый трассируемый макрос имеет соответствующий
ENABLEDмакрос.