70.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 активирован, запрос, соответствующий условию триггера, выполняется в неявно созданной подтранзакции. В случае перезапуска это помогает выполнять очистку после предыдущего запроса и освобождать системные ресурсы, такие как память или дисковое пространство.
70.1.1. Триггеры AQE #
Следующие триггеры могут прервать запрос и запустить AQE:
Время выполнения запроса: триггер срабатывает, если запрос выполняется дольше, чем указано в параметре конфигурации aqe_sql_execution_time_trigger.
Количество обработанных кортежей узлов: триггер срабатывает, когда количество таких кортежей превышает число, обычно ожидаемое планировщиком, которое умножается на значение параметра конфигурации aqe_rows_underestimation_rate_trigger.
Потребление памяти рабочим процессом: триггер срабатывает, когда потребление памяти рабочим процессом превышает значение параметра конфигурации aqe_backend_memory_used_trigger и срабатывает триггер количества обработанных кортежей узлов.
Ручной триггер: срабатывает, когда пользователь вызывает функцию
replan_signalиз другого сеанса. В отличие от остальных триггеров, этот триггер не может быть отключён.
70.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.
70.1.3. Интеграция с pgpro_multiplan #
AQE может работать совместно с расширением pgpro_multiplan, предоставляя следующие возможности:
управление планами в реальном времени, то есть автоматическое добавление планов, созданных с помощью AQE, в список разрешённых планов расширения pgpro_multiplan
возможность переопределять и настраивать значения триггеров для отдельных запросов
сбор статистики AQE для всех операторов, которые рассматриваются для переоптимизации
70.1.4. Пример #
Пример 70.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)
70.1. How It Works #
If an AQE trigger fires during query execution, the partially executed query is paused, and AQE attempts to reoptimize it. Information about the query statement, node cardinalities of non-parameterized nodes and parameterized nodes with actual cardinalities larger than predicted is used to choose a new plan and is stored for further reoptimizations. Then the query is rerun using the new plan. AQE triggers may be disabled for future reoptimizations of the same query in the following situations:
No new information was gathered during execution. In this case, the output of
EXPLAIN ANALYZEincludes the text:AQE Deactivation Reason: no new learning data.The newly generated plan matches a plan that was already tried. In this case, the output of
EXPLAIN ANALYZEincludes the text:AQE Deactivation Reason: repeated plan.The maximum number of reruns, set by the aqe_max_reruns configuration parameter, was reached. In this case, the output of
EXPLAIN ANALYZEincludes the text:AQE Deactivation Reason: rerun limit reached.An extension, such as pgpro_multiplan, disabled further reoptimizations. In this case, the output of
EXPLAIN ANALYZEincludes the text:AQE Deactivation Reason: external deactivation.
In such cases, the query execution continues without interruption.
If an AQE trigger is activated, a query that meets the trigger condition is executed in an implicitly created subtransaction. In case of rerunning, this helps in cleaning up after the previous execution and releasing system resources, such as memory or disk space.
70.1.1. AQE Triggers #
The following triggers can interrupt a query and launch AQE:
Query execution time: the trigger fires when the query runs longer than the value of the aqe_sql_execution_time_trigger configuration parameter.
Number of processed node tuples: the trigger fires when this number exceeds the number normally expected by the planner, which is multiplied by the value of the aqe_rows_underestimation_rate_trigger configuration parameter.
Backend memory consumption: the trigger fires when memory consumption of a backend exceeds the value of the aqe_backend_memory_used_trigger configuration parameter and the number of processed node tuples trigger fired.
Manual trigger: the trigger fires when the user calls
replan_signalfrom another session. Unlike other triggers, this trigger cannot be deactivated.
70.1.2. Viewing AQE Details #
If aqe_show_details is on, information related to AQE is included in the EXPLAIN ANALYZE output. The SUMMARY section additionally includes the following characteristics:
AQE Active— whether AQE was active by the end of the query execution.Table Entries— number of tables in the query, including subqueries.Controlled Statements— number of statements that were reoptimized during execution of the query.AQE Reruns— total number of the query reruns.Total Time Elapsed— total time of the query execution including the time of all reruns. This property is only displayed if the query was reoptimized and rerun at least once.Final Run Planning Time— planning time during last AQE reoptimization. This property is only displayed if the query was reoptimized and rerun at least once.AQE Deactivation Reason— what caused AQE to be disabled. Possible values are self-explanatory:rerun limit reached,repeated plan,no new learning data, orexternal deactivation. Displayed only if AQE was not active by the end of the query execution.
For each plan node, the following characteristics are included:
NodeSign— a 64-bit signature (hash) of the node that uniquely identifies the node of the query plan. Not all nodes are signed, but only those where errors of the optimizer estimation model can cause errors in searching the optimal query plan.Cardinality— cardinality of the plan node achieved in previous executions of the query. Only available with theVERBOSEparameter.Groups Number— number of groups computed for this plan node from previous executions of the query. Only available with theVERBOSEparameter.
70.1.3. Integration with pgpro_multiplan #
AQE can work in conjunction with the pgpro_multiplan extension providing the following features:
real-time plan management, that is, automatic addition of plans produced by AQE to a list of allowed plans used by pgpro_multiplan
the ability to override and adjust trigger values for individual queries
collecting AQE statistics for all statements considered feasible for reoptimization
70.1.4. Example #
Example 70.1. Using AQE
The following example illustrates 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 is disabled 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 is enabled with node tuples underestimation trigger 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;
Output without 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)
Output with 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)