68.1. Как это работает #

Если во время выполнения запроса срабатывает триггер AQE, частично выполненный запрос приостанавливается, и AQE пытается его переоптимизировать. Информация об операторе запроса, мощностях узлов непараметризованных узлов и параметризованных узлах с фактическими мощностями, превышающими прогнозы, используется для выбора нового плана и сохраняется для дальнейших переоптимизаций. Затем запрос запускается заново с использованием нового плана. Триггеры AQE могут быть отключены для будущих переоптимизаций того же запроса в следующих ситуациях:

  • Во время выполнения не были собраны новые данные.

  • Новый созданный план соответствует уже опробованному.

  • Достигнуто максимальное количество перезапусков, заданное параметром конфигурации aqe_max_reruns.

В таких случаях выполнение запроса продолжается без прерывания.

Если триггер AQE активирован, запрос, соответствующий условию триггера, выполняется в неявно созданной подтранзакции. В случае перезапуска это помогает выполнять очистку после предыдущего запроса и освобождать системные ресурсы, такие как память или дисковое пространство.

68.1.1. Триггеры AQE #

Следующие триггеры могут прервать запрос и запустить AQE:

  • Время выполнения запроса: триггер срабатывает, если запрос выполняется дольше, чем указано в параметре конфигурации aqe_sql_execution_time_trigger.

  • Количество обработанных кортежей узлов: триггер срабатывает, когда количество таких кортежей превышает число, обычно ожидаемое планировщиком, которое умножается на значение параметра конфигурации aqe_rows_underestimation_rate_trigger.

  • Потребление памяти рабочим процессом: триггер срабатывает, когда потребление памяти рабочим процессом превышает значение параметра конфигурации aqe_backend_memory_used_trigger и срабатывает триггер количества обработанных кортежей узлов.

68.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 — время планирования последней переоптимизации при адаптивном выполнении запроса. Это свойство отображается только в том случае, если запрос был переоптимизирован и перезапущен хотя бы один раз.

Для каждого узла плана включены следующие характеристики:

  • NodeSign — 64-битная подпись (хеш) узла, которая однозначно идентифицирует узел плана запроса. Подписаны не все узлы, а только те, где ошибки модели оценки оптимизатора могут приводить к ошибкам в поиске оптимального плана запроса.

  • Cardinality — количество кортежей узла плана, достигнутое при предыдущих выполнениях запроса. Доступно только с параметром VERBOSE.

  • Groups Number — количество групп, вычисленное для этого узла плана в результате предыдущих выполнений запроса. Доступно только с параметром VERBOSE.

68.1.3. Пример #

Пример 68.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)