69.1. Как это работает #
Если во время выполнения запроса срабатывает триггер AQE, частично выполненный запрос приостанавливается, и AQE пытается его переоптимизировать. Информация об операторе запроса, мощностях узлов непараметризованных узлов и параметризованных узлах с фактическими мощностями, превышающими прогнозы, используется для выбора нового плана и сохраняется для дальнейших переоптимизаций. Затем запрос запускается заново с использованием нового плана. Триггеры AQE могут быть отключены для будущих переоптимизаций того же запроса в следующих ситуациях:
Во время выполнения не было получено новых данных обучения. В этом случае вывод команды
EXPLAIN ANALYZEсодержит сообщение:AQE Deactivation Reason: no new learning data(причина отключения AQE: отсутствуют новые данные обучения).Сформированный повторно план совпадает с уже использованным. В этом случае вывод команды
EXPLAIN ANALYZEсодержит сообщение:AQE Deactivation Reason: repeated plan(причина отключения AQE: повторный план).Достигнуто максимальное количество перезапусков, заданное параметром конфигурации aqe_max_reruns. В этом случае вывод команды
EXPLAIN ANALYZEсодержит сообщение:AQE Deactivation Reason: rerun limit reached(причина отключения AQE: достигнут лимит перезапусков).Расширение, например pgpro_multiplan, отключило дальнейшее перепланирование. В этом случае вывод команды
EXPLAIN ANALYZEсодержит сообщение:AQE Deactivation Reason: external deactivation(причина отключения AQE: внешняя деактивация).
В таких случаях выполнение запроса продолжается без прерывания.
Если триггер AQE активирован, запрос, соответствующий условию триггера, выполняется в неявно созданной подтранзакции. В случае перезапуска это помогает выполнять очистку после предыдущего запроса и освобождать системные ресурсы, такие как память или дисковое пространство.
69.1.1. Триггеры AQE #
Следующие триггеры могут прервать запрос и запустить AQE:
Время выполнения запроса: триггер срабатывает, если запрос выполняется дольше, чем указано в параметре конфигурации aqe_sql_execution_time_trigger.
Количество обработанных кортежей узлов: триггер срабатывает, когда количество таких кортежей превышает число, обычно ожидаемое планировщиком, которое умножается на значение параметра конфигурации aqe_rows_underestimation_rate_trigger.
Потребление памяти рабочим процессом: триггер срабатывает, когда потребление памяти рабочим процессом превышает значение параметра конфигурации aqe_backend_memory_used_trigger и срабатывает триггер количества обработанных кортежей узлов.
Ручной триггер: срабатывает, когда пользователь вызывает функцию
replan_signalиз другого сеанса. В отличие от остальных триггеров, этот триггер не может быть отключён.
69.1.2. Подробности адаптивного выполнения запросов #
Если параметр aqe_show_details включён, информация, связанная с AQE, добавляется в вывод команды EXPLAIN ANALYZE. Раздел SUMMARY дополнительно включает следующие характеристики:
AQE Active— был ли AQE активен к моменту завершения выполнения запроса.Table Entries— количество таблиц в запросе, включая подзапросы.Controlled Statements— количество операторов, которые были переоптимизированы во время выполнения запроса.AQE Reruns— общее количество перезапусков запроса.Total Time Elapsed— общее время выполнения запроса, включая время всех попыток перезапуска. Это свойство отображается только в том случае, если запрос был переоптимизирован и перезапущен хотя бы один раз.Final Run Planning Time— время планирования последней переоптимизации при адаптивном выполнении запроса. Это свойство отображается только в том случае, если запрос был переоптимизирован и перезапущен хотя бы один раз.AQE Deactivation Reason— причина отключения AQE. Возможные значения не требуют пояснений:rerun limit reached(достигнут лимит перезапусков),repeated plan(повторный план),no new learning data(отсутствуют новые данные для обучения) илиexternal deactivation(внешняя деактивация). Отображается только в том случае, если AQE не был активен к моменту завершения выполнения запроса.
Для каждого узла плана включены следующие характеристики:
NodeSign— 64-битная подпись (хеш) узла, которая однозначно идентифицирует узел плана запроса. Подписаны не все узлы, а только те, где ошибки модели оценки оптимизатора могут приводить к ошибкам в поиске оптимального плана запроса.Cardinality— количество кортежей узла плана, достигнутое при предыдущих выполнениях запроса. Доступно только с параметромVERBOSE.Groups Number— количество групп, вычисленное для этого узла плана в результате предыдущих выполнений запроса. Доступно только с параметромVERBOSE.
69.1.3. Интеграция с pgpro_multiplan #
AQE может работать совместно с расширением pgpro_multiplan, предоставляя следующие возможности:
Автоматическое добавление планов, созданных с помощью AQE, в список разрешённых планов, используемый расширением pgpro_multiplan.
Возможность переопределять и настраивать значения триггеров для отдельных запросов.
Сбор статистики AQE для всех операторов, которые рассматриваются для переоптимизации.
Используйте параметр конфигурации pgpro_multiplan.aqe_mode для включения функциональности, связанной с AQE.
69.1.4. Пример #
Пример 69.1. Использование AQE
В следующем примере показано использование AQE:
DROP TABLE IF EXISTS aqe_test; CREATE TABLE aqe_test WITH (autovacuum_enabled=off) AS SELECT x as x, x as y, x as z from generate_series(1, 100000) as x; INSERT INTO aqe_test SELECT 1, x, x FROM generate_series(1, 1000000) as x; CREATE INDEX ON aqe_test(x); CREATE INDEX ON aqe_test(y); ANALYZE; -- AQE отключено SET aqe_enable = false; EXPLAIN ANALYZE SELECT FROM aqe_test t1, aqe_test t2, aqe_test t3 WHERE t1.x = t2.x and t1.y = t3.y and t1.y < 100 and t1.z < 100 and t2.y < 100 and t2.z < 100 and t3.y < 100 and t3.z < 100; -- AQE включено триггером количества обработанных кортежей SET aqe_enable = true; SET aqe_rows_underestimation_rate_trigger = 2; EXPLAIN ANALYZE SELECT FROM aqe_test t1, aqe_test t2, aqe_test t3 WHERE t1.x = t2.x and t1.y = t3.y and t1.y < 100 and t1.z < 100 and t2.y < 100 and t2.z < 100 and t3.y < 100 and t3.z < 100;
Вывод без AQE:
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------------------
Nested Loop (cost=14.29..44.44 rows=1 width=0) (actual time=77.493..6778.074 rows=20196 loops=1)
-> Nested Loop (cost=13.86..35.98 rows=1 width=4) (actual time=77.472..6722.165 rows=10098 loops=1)
-> Index Scan using aqe_test_y_idx on aqe_test t1 (cost=0.43..18.52 rows=1 width=8) (actual time=0.015..0.271 rows=198 loops=1)
Index Cond: (y < 100)
Filter: (z < 100)
-> Bitmap Heap Scan on aqe_test t2 (cost=13.43..17.45 rows=1 width=4) (actual time=33.924..33.940 rows=51 loops=198)
Recheck Cond: ((y < 100) AND (t1.x = x))
Filter: (z < 100)
Heap Blocks: exact=298
-> BitmapAnd (cost=13.43..13.43 rows=1 width=0) (actual time=33.917..33.917 rows=0 loops=198)
-> Bitmap Index Scan on aqe_test_y_idx (cost=0.00..5.97 rows=206 width=0) (actual time=0.016..0.016 rows=198 loops=198)
Index Cond: (y < 100)
-> Bitmap Index Scan on aqe_test_x_idx (cost=0.00..7.21 rows=371 width=0) (actual time=33.898..33.898 rows=505052 loops=198)
Index Cond: (x = t1.x)
-> Index Scan using aqe_test_y_idx on aqe_test t3 (cost=0.43..8.45 rows=1 width=4) (actual time=0.004..0.005 rows=2 loops=10098)
Index Cond: ((y = t1.y) AND (y < 100))
Filter: (z < 100)
Planning Time: 0.804 ms
Execution Time: 6779.473 ms
(19 rows)
Вывод с AQE:
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------------------------
Merge Join (cost=1.28..718.25 rows=20196 width=0) (actual time=0.040..31.452 rows=20196 loops=1)
NodeSign: 13451128626708613266
Merge Cond: (t1.y = t3.y)
-> Nested Loop (cost=0.85..622.63 rows=9998 width=4) (actual time=0.029..8.957 rows=10098 loops=1)
NodeSign: 3624220923067346440
Join Filter: (t1.x = t2.x)
Rows Removed by Join Filter: 29106
-> Index Scan using aqe_test_y_idx on aqe_test t1 (cost=0.43..18.52 rows=197 width=8) (actual time=0.014..0.181 rows=198 loops=1)
NodeSign: 5973269849475827818
Index Cond: (y < 100)
Filter: (z < 100)
-> Materialize (cost=0.43..19.51 rows=198 width=4) (actual time=0.000..0.013 rows=198 loops=198)
NodeSign: subordinate
-> Index Scan using aqe_test_y_idx on aqe_test t2 (cost=0.43..18.52 rows=198 width=4) (actual time=0.010..0.182 rows=198 loops=1)
NodeSign: 480658296932687531
Index Cond: (y < 100)
Filter: (z < 100)
-> Index Scan using aqe_test_y_idx on aqe_test t3 (cost=0.43..18.52 rows=19996 width=4) (actual time=0.010..16.026 rows=20096 loops=1)
NodeSign: 12407239917974993641
Index Cond: (y < 100)
Filter: (z < 100)
Planning Time: 214.610 ms
Execution Time: 33.130 ms
AQE Active: true
Table Entries: 3
Controlled Statements: 1
AQE Reruns: 3
Final Run Planning Time: 0.591 ms
Total Time Elapsed: 247.740 ms
(29 rows)