F.46. pg_query_state
Модуль pg_query_state позволяет проверять текущее состояние выполнения запросов в работающем обслуживающем процессе.
F.46.1. Обзор
После этапа планирования/оптимизации для каждого неслужебного запроса (SELECT/INSERT/ UPDATE/DELETE) выстраивается дерево плана. Это императивное представление декларативного SQL-запроса. Запрос EXPLAIN ANALYZE позволяет просматривать статистику выполнения, собранную с каждого узла этого дерева плана, например общее время выполнения, число переданных верхним узлам строк и т. д. Однако эта статистика собирается только после выполнения запроса. Модуль pg_query_state позволяет просматривать актуальную статистику запроса, выполняющегося во внешнем обслуживающем процессе. Формат вывода практически идентичен выводу обычного запроса EXPLAIN ANALYZE. Таким образом, можно отслеживать сам процесс выполнения запроса. Этот модуль может исследовать внешний обслуживающий процесс и определять его фактическое состояние. Это особенно полезно, когда обслуживающий процесс выполняет очень сложный запрос или зависает.
F.46.2. Сценарии использования
Модуль pg_query_state позволяет:
Выявлять сложные запросы, что возможно в совокупности с другими средствами мониторинга.
Наблюдать за выполнением запросов.
F.46.3. Установка
Чтобы установить pg_query_state, выполните следующие действия:
Добавьте
pg_query_stateв переменнуюshared_preload_librariesв файлеpostgresql.conf:shared_preload_libraries = 'pg_query_state'
Перезагрузите сервер баз данных, чтобы изменения вступили в силу.
Создайте расширение pg_query_state:
CREATE EXTENSION pg_query_state;
Чтобы обновить модуль pg_query_state, выполните команду
ALTER EXTENSION UPDATE.
F.46.4. Функции
pg_query_state (pidinteger,verboseboolean,costsboolean,timingboolean,buffersboolean,triggersboolean,formattext) returns tableПолучает состояние текущего запроса от обслуживающего процесса с заданным
pid. Параллельные запросы могут порождать несколько рабочих процессов, а при вызове функций создаются вложенные подзапросы, поэтому состояние выполнения запроса может быть представлено в виде стека выполняющихся запросов. По этой причине функцияpg_query_stateвозвращает значение следующего типа:TABLE (
pidinteger,frame_numberinteger,query_texttext,plantext,leader_pidinteger)Он представляет иерархическую структуру, состоящую из ведущего процесса и порождённых им рабочих процессов, идентифицируемых по
pid. Каждый рабочий процесс ссылается на свой ведущий процесс по значениюleader_pid. Для ведущего процесса это значение —NULL. Состояние каждого процесса представляется в виде стека вызовов функций. Для каждого кадра этого стека устанавливается соответствие по столбцамframe_number(номер кадра, начиная с нуля),query_text(текст запроса) иplan(план) со столбцами текущей статистики.Таким образом, можно видеть состояние основного запроса и запросов, создаваемых вызовами функций, для ведущего процесса и всех порождённых им рабочих процессов.
В процессе выполнения запроса некоторые узлы дерева плана могут делать несколько циклов полного выполнения. Поэтому статистика по таким узлам состоит из двух частей: средняя статистика по предыдущим циклам, как в выводе
EXPLAIN ANALYZE, и статистика для текущего ещё не законченного цикла.Эта функция имеет следующие необязательные аргументы:
verbose— если задано значениеtrue, функция использует командуEXPLAIN VERBOSEдля вывода плана. Значение по умолчанию —false.costs— если задано значениеtrue, функция выводит стоимость выполнения для каждого узла. Значение по умолчанию —false.timing— если задано значениеtrue, функция выводит статистику времени выполнения для каждого узла. Если сбор статистики отключён, выводится соответствующее сообщение. Значение по умолчанию —false.buffers— если задано значениеtrue, функция выводит статистику использования буферов. Если сбор статистики отключён, выводится соответствующее сообщение. Значение по умолчанию —false.triggers— если задано значениеtrue, функция включает статистику срабатывания триггеров в результирующие деревья планов. Значение по умолчанию —false.format— форматEXPLAIN, который будет использован для вывода плана. Аргумент может принимать значенияtext,xml,jsonиyaml. Значение по умолчанию —text.
Если вызываемый обслуживающий процесс не выполняет запрос, функция возвращает информационное сообщение о его состоянии, полученном из представления pg_stat_activity, если такая информация доступна.
Эту функцию могут вызывать только члены роли, которая владеет обслуживающим процессом, или суперпользователи.
pg_progress_bar (pidinteger) returns floatВозвращает текущий прогресс выполнения запроса. Сначала функция получает от обслуживающего процесса с заданным
pidсостояние текущего запроса. Затем на основании информации из всех узлов плана она собирает общее фактическое и ожидаемое число строк и вычисляет их соотношение для всего дерева выполнения запроса. В результате функция возвращает числовое значение в диапазоне от0до1. Оно обозначает прогресс выполнения запроса. Эта функция полезна для визуального представления прогресса выполнения запроса.Если информация о состоянии текущего запроса отсутствует или вычислить прогресс выполнения невозможно, выводится соответствующее сообщение.
Эту функцию могут вызывать только члены роли, которая владеет обслуживающим процессом, или суперпользователи.
pg_progress_bar_visual (pidinteger,delayinteger) returns voidЦиклически вычисляет прогресс выполнения запроса и возвращает его в текстовом формате. Функция получает от обслуживающего процесса с заданным
pidсостояние текущего запроса через интервалы времени, определённые в параметреdelay(в секундах).Эта функция является циклическим вариантом функции pg_progress_bar, но возвращает
void.Если информация о текущем состоянии запроса отсутствует или вычислить прогресс выполнения невозможно, выводится соответствующее сообщение.
Эту функцию могут вызывать только члены роли, которая владеет обслуживающим процессом, или суперпользователи.
F.46.5. Параметры конфигурации
Для управления самим модулем и сбором статистики во время выполнения запросов доступны несколько параметров конфигурации. Их значения должны устанавливаться на вызываемой стороне до выполнения запросов, состояние которых необходимо получить.
pg_query_state.enable(boolean)Включает или отключает модуль pg_query_state.
Значение по умолчанию —
true.pg_query_state.enable_timing(boolean)Включает или отключает сбор статистики времени выполнения для каждого узла. Если параметр отключён, вызывающая сторона не сможет получить эту статистику.
Значение по умолчанию —
false.pg_query_state.enable_buffers(boolean)Включает или отключает сбор статистики использования буферов. Если параметр отключён, вызывающая сторона не сможет получить эту статистику.
Значение по умолчанию —
false.
F.46.6. Примеры использования
Установите максимальное число параллельных рабочих процессов для узла Gather равным 2:
postgres=# SET max_parallel_workers_per_gather = 2;
Предположим, что один обслуживающий процесс с идентификатором 49265 выполняет простой запрос:
postgres=# SELECT pg_backend_pid(); pg_backend_pid ---------------- 49265 (1 row) postgres=# SELECT count(*) FROM foo JOIN bar ON foo.c1=bar.c1;
Другой обслуживающий процесс может извлечь промежуточное состояние выполнения этого запроса:
postgres=# \x
postgres=# SELECT * FROM pg_query_state(49265);
-[ RECORD 1 ]+-------------------------------------------------------------------------------------------------------------------------
pid | 49265
frame_number | 0
query_text | SELECT count(*) FROM foo JOIN bar ON foo.c1=bar.c1;
plan | Finalize Aggregate (Current loop: actual rows=0, loop number=1) +
| -> Gather (Current loop: actual rows=0, loop number=1) +
| Workers Planned: 2 +
| Workers Launched: 2 +
| -> Partial Aggregate (Current loop: actual rows=0, loop number=1) +
| -> Nested Loop (Current loop: actual rows=12, loop number=1) +
| Join Filter: (foo.c1 = bar.c1) +
| Rows Removed by Join Filter: 5673232 +
| -> Parallel Seq Scan on foo (Current loop: actual rows=12, loop number=1) +
| -> Seq Scan on bar (actual rows=500000 loops=11) (Current loop: actual rows=173244, loop number=12)
leader_pid | (null)
-[ RECORD 2 ]+-------------------------------------------------------------------------------------------------------------------------
pid | 49324
frame_number | 0
query_text | <parallel query>
plan | Partial Aggregate (Current loop: actual rows=0, loop number=1) +
| -> Nested Loop (Current loop: actual rows=10, loop number=1) +
| Join Filter: (foo.c1 = bar.c1) +
| Rows Removed by Join Filter: 4896779 +
| -> Parallel Seq Scan on foo (Current loop: actual rows=10, loop number=1) +
| -> Seq Scan on bar (actual rows=500000 loops=9) (Current loop: actual rows=396789, loop number=10)
leader_pid | 49265
-[ RECORD 3 ]+-------------------------------------------------------------------------------------------------------------------------
pid | 49323
frame_number | 0
query_text | <parallel query>
plan | Partial Aggregate (Current loop: actual rows=0, loop number=1) +
| -> Nested Loop (Current loop: actual rows=11, loop number=1) +
| Join Filter: (foo.c1 = bar.c1) +
| Rows Removed by Join Filter: 5268783 +
| -> Parallel Seq Scan on foo (Current loop: actual rows=11, loop number=1) +
| -> Seq Scan on bar (actual rows=500000 loops=10) (Current loop: actual rows=268794, loop number=11)
leader_pid | 49265В примере выше работающий обслуживающий процесс порождает два параллельных рабочих процесса с идентификаторами 49324 и 49323. Значения в столбце leader_pid показывают, что эти процессы относятся к основному обслуживающему процессу. Узел Seq Scan показывает статистику по выполненным циклам (среднее число строк, переданное узлу Nested Loop, и число выполненных циклов) и статистику по текущему циклу. Другие узлы показывают статистику только по текущему циклу, так как он является первым (loop number=1).
Предположим, что первый обслуживающий процесс вызывает функцию:
postgres=# SELECT n_join_foo_bar();
Другой обслуживающий процесс может получить следующий вывод:
postgres=# SELECT * FROM pg_query_state(49265);
-[ RECORD 1 ]+------------------------------------------------------------------------------------------------------------------
pid | 49265
frame_number | 0
query_text | SELECT n_join_foo_bar();
plan | Result (Current loop: actual rows=0, loop number=1)
leader_pid | (null)
-[ RECORD 2 ]+------------------------------------------------------------------------------------------------------------------
pid | 49265
frame_number | 1
query_text | SELECT (select count(*) FROM foo JOIN bar ON foo.c1=bar.c1)
plan | Result (Current loop: actual rows=0, loop number=1) +
| InitPlan 1 (returns $0) +
| -> Aggregate (Current loop: actual rows=0, loop number=1) +
| -> Nested Loop (Current loop: actual rows=51, loop number=1) +
| Join Filter: (foo.c1 = bar.c1) +
| Rows Removed by Join Filter: 51636304 +
| -> Seq Scan on bar (Current loop: actual rows=52, loop number=1) +
| -> Materialize (actual rows=1000000 loops=51) (Current loop: actual rows=636355, loop number=52)+
| -> Seq Scan on foo (Current loop: actual rows=1000000, loop number=1)
leader_pid | (null)Первая строка соответствует вызову функции, вторая — запросу, выполняемому в теле этой функции.
Результирующие планы можно получить в других форматах, например JSON:
postgres=# SELECT * FROM pg_query_state(pid := 49265, format := 'json');
-[ RECORD 1 ]+------------------------------------------------------------
pid | 49265
frame_number | 0
query_text | SELECT * FROM n_join_foo_bar();
plan | { +
| "Plan": { +
| "Node Type": "Function Scan", +
| "Parallel Aware": false, +
| "Function Name": "n_join_foo_bar", +
| "Alias": "n_join_foo_bar", +
| "Current loop": { +
| "Actual Loop Number": 1, +
| "Actual Rows": 0 +
| } +
| } +
| }
leader_pid | (null)
-[ RECORD 2 ]+------------------------------------------------------------
pid | 49265
frame_number | 1
query_text | SELECT (SELECT count(*) FROM foo JOIN bar ON foo.c1=bar.c1)
plan | { +
| "Plan": { +
| "Node Type": "Result", +
| "Parallel Aware": false, +
| "Current loop": { +
| "Actual Loop Number": 1, +
| "Actual Rows": 0 +
| }, +
| "Plans": [ +
| { +
| "Node Type": "Aggregate", +
| "Strategy": "Plain", +
| "Partial Mode": "Simple", +
| "Parent Relationship": "InitPlan", +
| "Subplan Name": "InitPlan 1 (returns $0)", +
| "Parallel Aware": false, +
| "Current loop": { +
| "Actual Loop Number": 1, +
| "Actual Rows": 0 +
| }, +
| "Plans": [ +
| { +
| "Node Type": "Nested Loop", +
| "Parent Relationship": "Outer", +
| "Parallel Aware": false, +
| "Join Type": "Inner", +
| "Current loop": { +
| "Actual Loop Number": 1, +
| "Actual Rows": 610 +
| }, +
| "Join Filter": "(foo.c1 = bar.c1)", +
| "Rows Removed by Join Filter": 610072944, +
| "Plans": [ +
| { +
| "Node Type": "Seq Scan", +
| "Parent Relationship": "Outer", +
| "Parallel Aware": false, +
| "Relation Name": "bar", +
| "Alias": "bar", +
| "Current loop": { +
| "Actual Loop Number": 1, +
| "Actual Rows": 611 +
| } +
| }, +
| { +
| "Node Type": "Materialize", +
| "Parent Relationship": "Inner", +
| "Parallel Aware": false, +
| "Actual Rows": 1000000, +
| "Actual Loops": 610, +
| "Current loop": { +
| "Actual Loop Number": 611, +
| "Actual Rows": 73554 +
| }, +
| "Plans": [ +
| { +
| "Node Type": "Seq Scan", +
| "Parent Relationship": "Outer", +
| "Parallel Aware": false, +
| "Relation Name": "foo", +
| "Alias": "foo", +
| "Current loop": { +
| "Actual Loop Number": 1, +
| "Actual Rows": 1000000 +
| } +
| } +
| ] +
| } +
| ] +
| } +
| ] +
| } +
| ] +
| } +
| }
leader_pid | (null)F.46.7. Примеры использования индикатора выполнения запроса
Первый обслуживающий процесс выполняет следующий запрос:
postgres=# INSERT INTO table_name SELECT generate_series(1,10000000);
С помощью другого обслуживающего процесса можно проверить прогресс выполнения этого запроса:
postgres=# SELECT pid FROM pg_stat_activity WHERE query LIKE 'insert%';
pid
-------
23877
(1 row)
postgres=# SELECT pg_progress_bar(23877);
pg_progress_bar
-----------------
0.6087927
(1 row)Чтобы визуально отслеживать прогресс выполнения, выполните следующий запрос:
postgres=# SELECT pg_progress_bar_visual(23877, 1);
Progress = 0.043510
Progress = 0.085242
Progress = 0.124921
Progress = 0.168168
Progress = 0.213803
Progress = 0.250362
Progress = 0.292632
Progress = 0.331454
Progress = 0.367509
Progress = 0.407450
Progress = 0.448646
Progress = 0.488171
Progress = 0.530559
Progress = 0.565558
Progress = 0.608039
Progress = 0.645778
Progress = 0.654842
Progress = 0.699006
Progress = 0.735760
Progress = 0.787641
Progress = 0.832160
Progress = 0.871077
Progress = 0.911858
Progress = 0.956362
Progress = 0.995097
Progress = 1.000000
pg_progress_bar_visual
------------------------
1
(1 row)