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

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

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

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

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

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

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

77.1.1. Триггеры перепланирования #

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

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

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

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

77.1.2. Просмотр подробностей о перепланировании #

Если параметр replan_show_signature включён, информация, связанная с перепланированием, добавляется в вывод команды EXPLAIN ANALYZE. Раздел SUMMARY дополнительно включает следующие характеристики:

  • Replan Active — срабатывал ли триггер перепланирования во время выполнения запроса.

  • Table Entries — количество таблиц в запросе, включая подзапросы.

  • Controlled Statements — количество операторов, которые были перепланированы во время выполнения запроса.

  • Replanning Attempts — общее количество перезапусков/попыток перепланирования запроса.

  • Total Time Elapsed — общее время выполнения запроса, включая время всех попыток перезапуска/перепланирования. Это свойство отображается только в том случае, если запрос был переоптимизирован и перезапущен хотя бы один раз.

  • Final Run Planning Time — время планирования последней переоптимизации при перепланировании. Это свойство отображается только в том случае, если запрос был переоптимизирован и перезапущен хотя бы один раз.

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

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

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

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

77.1.3. Пример #

Пример 77.1. Использование перепланирования

В следующем примере показано использование перепланирования:

  DROP TABLE IF EXISTS replan_test;
  CREATE TABLE replan_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 replan_test SELECT 1, x, x FROM generate_series(1, 1000000) as x;

  CREATE INDEX ON replan_test(x);
  CREATE INDEX ON replan_test(y);
  ANALYZE;

  set replan_show_signature = true;

  -- Перепланирование отключено
  SET replan_enable = false;
  EXPLAIN ANALYZE
  SELECT FROM replan_test t1, replan_test t2, replan_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;

  -- Перепланирование включено триггером количества обработанных кортежей
  SET replan_enable = true;
  SET replan_overrun_limit = 2;
  EXPLAIN ANALYZE
  SELECT FROM replan_test t1, replan_test t2, replan_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;
  

Вывод без перепланирования:

                                                                      QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------
 Nested Loop  (cost=14.30..45.18 rows=1 width=0) (actual time=73.677..6848.136 rows=20196 loops=1)
   ->  Nested Loop  (cost=13.87..36.72 rows=1 width=4) (actual time=73.654..6792.731 rows=10098 loops=1)
         ->  Index Scan using replan_test_y_idx on replan_test t1  (cost=0.43..19.25 rows=1 width=8) (actual time=0.014..0.271 rows=198 loops=1)
               Index Cond: (y < 100)
               Filter: (z < 100)
         ->  Bitmap Heap Scan on replan_test t2  (cost=13.44..17.46 rows=1 width=4) (actual time=34.278..34.295 rows=51 loops=198)
               Recheck Cond: ((y < 100) AND (t1.x = x))
               Filter: (z < 100)
               Heap Blocks: exact=298
               ->  BitmapAnd  (cost=13.44..13.44 rows=1 width=0) (actual time=34.268..34.268 rows=0 loops=198)
                     ->  Bitmap Index Scan on replan_test_y_idx  (cost=0.00..6.08 rows=221 width=0) (actual time=0.016..0.016 rows=198 loops=198)
                           Index Cond: (y < 100)
                     ->  Bitmap Index Scan on replan_test_x_idx  (cost=0.00..7.11 rows=357 width=0) (actual time=34.248..34.248 rows=505052 loops=198)
                           Index Cond: (x = t1.x)
   ->  Index Scan using replan_test_y_idx on replan_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.722 ms
 Execution Time: 6849.539 ms
(19 rows)

Вывод с перепланированием:

                                                                      QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------
 Merge Join  (cost=1.28..720.49 rows=20196 width=0) (actual time=0.038..30.495 rows=20196 loops=1)
   NodeSign: 16399548477598347697
   Merge Cond: (t1.y = t3.y)
   ->  Nested Loop  (cost=0.85..624.08 rows=9998 width=4) (actual time=0.027..8.577 rows=10098 loops=1)
         NodeSign: 6806589677965256871
         Join Filter: (t1.x = t2.x)
         Rows Removed by Join Filter: 29106
         ->  Index Scan using replan_test_y_idx on replan_test t1  (cost=0.43..19.25 rows=197 width=8) (actual time=0.014..0.180 rows=198 loops=1)
               NodeSign: 17234727896600901988
               Index Cond: (y < 100)
               Filter: (z < 100)
         ->  Materialize  (cost=0.43..20.24 rows=198 width=4) (actual time=0.000..0.013 rows=198 loops=198)
               NodeSign: subordinate
               ->  Index Scan using replan_test_y_idx on replan_test t2  (cost=0.43..19.25 rows=198 width=4) (actual time=0.010..0.180 rows=198 loops=1)
                     NodeSign: 8157818216567004834
                     Index Cond: (y < 100)
                     Filter: (z < 100)
   ->  Index Scan using replan_test_y_idx on replan_test t3  (cost=0.43..19.25 rows=19996 width=4) (actual time=0.009..15.417 rows=20096 loops=1)
         NodeSign: 12097487659300777104
         Index Cond: (y < 100)
         Filter: (z < 100)
 Planning Time: 252.050 ms
 Execution Time: 32.169 ms
 Replan Active: true
 Table Entries: 3
 Controlled Statements: 1
 Replanning Attempts: 3
 Final Run Planning Time: 0.612 ms
 Total Time Elapsed: 284.219 ms
(29 rows)