G.3. pgpro_multiplan — сохранение планов выполнения запросов и подготовленных операторов для последующего использования #

G.3.1. Описание #

Расширение pgpro_multiplan позволяет сохранять планы выполнения запросов и использовать их при последующем выполнении тех же запросов, что помогает избежать повторной оптимизации идентичных запросов. Можно также использовать это расширение для фиксации определённого плана выполнения, если план, выбранный планировщиком, по каким-то причинам не подходит. pgpro_multiplan работает подобно Oracle SQL Plan Management (управление SQL-планами).

Это расширение также позволяет хранить глобальные подготовленные операторы, созданные в разных сеансах баз данных, и использовать эти операторы в нескольких сеансах.

G.3.2. Установка #

Расширение pgpro_multiplan предоставляется вместе с Postgres Pro Enterprise в виде отдельного пакета pgpro-multiplan-ent-17 (подробные инструкции по установке приведены в Главе 17). Чтобы включить pgpro_multiplan, выполните следующие действия:

  1. Добавьте имя библиотеки в переменную shared_preload_libraries в файле postgresql.conf:

    shared_preload_libraries = 'pgpro_multiplan'

    Обратите внимание, что имена библиотек в переменной shared_preload_libraries должны добавляться в определённом порядке. Совместимость pgpro_multiplan с другими расширениями описана в Подразделе G.3.7.

  2. Перезагрузите сервер баз данных, чтобы изменения вступили в силу.

    Чтобы убедиться, что библиотека pgpro_multiplan установлена правильно, вы можете выполнить следующую команду:

    SHOW shared_preload_libraries;
  3. Создайте расширение pgpro_multiplan, выполнив следующий запрос:

    CREATE EXTENSION pgpro_multiplan;

    Расширение pgpro_multiplan использует кеш разделяемой памяти, который инициализируется только при запуске сервера, поэтому данная библиотека также должна предзагружаться при запуске. Расширение pgpro_multiplan следует создать в каждой базе данных, где требуется управление запросами.

  4. Включите расширение pgpro_multiplan, которое выключено по умолчанию.

    Чтобы активировать один или несколько режимов, связанных с использованием различных планов и их статистикой, задайте параметр pgpro_multiplan.mode. За подробной информацией обратитесь к разделу Поддерживаемые режимы и типы планов.

    Чтобы разрешить использование глобальных подготовленных операторов, установите для параметра pgpro_multiplan.global_prepared_statements значение on.

    Эти параметры можно задать одним из следующих способов:

    • при запуске сервера (в файле postgresql.conf), чтобы применить их ко всем сеансам

    • в текущем сеансе с помощью команды SET

  5. При необходимости переноса данных pgpro_multiplan с главного на резервный сервер при помощи физической репликации, на обоих серверах необходимо задать значение параметра pgpro_multiplan.wal_rw=on. Также убедитесь, что на обоих серверах установлена одинаковая версия pgpro_multiplan, иначе репликация может работать некорректно.

G.3.3. Поддерживаемые режимы и типы планов #

Расширение pgpro_multiplan поддерживает следующие планы:

  • Замороженные планы: зафиксированные планы, которым отдаётся приоритет при выполнении соответствующих запросов. Запрос может иметь только один замороженный план. pgpro_multiplan поддерживает следующие типы замороженных планов:

    • serialized: сериализованное представление плана. Этот план конвертируется в выполняемый план при первом нахождении соответствующего запроса. Сериализованные планы остаются действительными до тех пор, пока не изменятся метаданные запроса (структуры таблиц, индексы и так далее). Например, если таблица, на которую ссылается замороженный план, будет пересоздана, этот план станет недействительным и будет игнорироваться. Сериализованные планы действительны только в текущей базе данных и не могут быть скопированы в другую, поскольку зависят от идентификаторов объектов (OID). По этой причине использовать сериализованные планы для временных таблиц не имеет смысла.

    • hintset: набор указаний, формируемый на основе плана выполнения в момент заморозки. Этот набор включает значения переменных окружения оптимизатора, отличных от используемых по умолчанию, типы соединений, порядок соединений и методы доступа к данным. Эти указания соответствуют указаниям, которые поддерживаются расширением pg_hint_plan. Для использования планов с наборами указаний необходимо включить расширение pg_hint_plan. При нахождении соответствующего замороженного запроса указания передаются pg_hint_plan для генерации выполняемого плана. Если расширение pg_hint_plan выключено, указания игнорируются и выполняется план, сформированный оптимизатором Postgres Pro. Планы с наборами указаний не зависят от идентификаторов объектов (OID) и остаются действительным при пересоздании таблиц, добавлении полей и других изменениях.

    • template: частный случай плана типа hintset. Такие планы применяются только для запросов с именами таблиц, которые соответствуют регулярному выражению, указанному в параметре конфигурации pgpro_multiplan.wildcards. Значение pgpro_multiplan.wildcards замораживается вместе с соответствующим запросом.

  • Базовые планы: наборы разрешённых планов, которые могут использоваться для выполнения запросов, если для них отсутствуют соответствующие замороженные планы. Как и замороженные планы типа hintset, базовые планы основаны на указаниях планировщика и требуют включения расширения pg_hint_plan. Если расширение pg_hint_plan выключено, указания игнорируются и выполняется план, сформированный оптимизатором Postgres Pro.

Используйте параметр конфигурации pgpro_multiplan.mode для указания списка включённых режимов и типов планов в, разделённых запятыми. По умолчанию для этого параметра указана пустая строка, означающая, что все режимы планов выключены.

G.3.4. Использование замороженных планов #

Чтобы разрешить использование замороженных планов, укажите значение frozen в параметре pgpro_multiplan.mode.

Чтобы заморозить план для дальнейшего использования, выполните следующие шаги:

  1. Зарегистрируйте запрос, план которого необходимо заморозить.

  2. Измените план выполнения запроса.

  3. Заморозьте план выполнения запроса.

G.3.4.1. Регистрация запроса #

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

  • С помощью функции pgpro_multiplan_register_query():

    SELECT pgpro_multiplan_register_query(query_string, parameter_type, ...);

    Здесь query_string — запрос с параметрами $n (аналогично PREPARE statement_name AS). Можно описать каждый тип параметра, используя необязательный аргумент функции parameter_type, или отказаться от явного определения типов параметров. В последнем случае Postgres Pro пытается определить тип каждого параметра из контекста. Эта функция возвращает уникальную пару sql_hash и const_hash. После этого pgpro_multiplan будет отслеживать выполнение запросов, соответствующих сохранённому шаблону параметризованного запроса.

    -- Создайте таблицу 'a'
    CREATE TABLE a AS (SELECT * FROM generate_series(1,30) AS x);
    CREATE INDEX ON a(x);
    ANALYZE;
    
    -- Зарегистрируйте запрос
    SELECT sql_hash, const_hash
    FROM pgpro_multiplan_register_query('SELECT count(*) FROM a
    WHERE x = 1 OR (x > $2 AND x < $1) OR x = $1', 'int', 'int');
           sql_hash        | const_hash
    -----------------------+-------------
      -6037606140259443514 | 2413041345
    (1 row)
  • С помощью параметра pgpro_multiplan.auto_tracking:

    -- Установите для pgpro_multiplan.auto_tracking значение on
    SET pgpro_multiplan.auto_tracking = on;
    
    -- Выполните EXPLAIN для непараметризованного запроса
    
    EXPLAIN SELECT count(*) FROM a WHERE x = 1 OR (x > 11 AND x < 22) OR x = 22;
    
    Custom Scan (MultiplanScan)  (cost=1.60..0.00 rows=1 width=8)
      Plan is: tracked
      SQL hash: 5393873830515778388
      Const hash: 0
      Plan hash: 0
      ->  Aggregate  (cost=1.60..1.61 rows=1 width=8)
            ->  Seq Scan on a  (cost=0.00..1.60 rows=2 width=0)
                  Filter: ((x = $1) OR ((x > $2) AND (x < $3)) OR (x = $4))
    
    -- Выключите pgpro_multiplan.auto_tracking
    SET pgpro_multiplan.auto_tracking = off;

Все зарегистрированные запросы хранятся в локальном кеше, к которому можно обращаться с помощью представления pgpro_multiplan_local_cache.

G.3.4.2. Изменение плана выполнения запроса #

План выполнения запроса можно изменить при помощи переменных оптимизатора, указаний pg_hint_plan при включённом расширении или других расширений, например aqo. Информация о совместимости pgpro_multiplan с другими расширениями представлена в Подразделе G.3.7.

G.3.4.3. Заморозка плана выполнения запроса #

Для заморозки изменённого плана выполнения запроса используйте функцию pgpro_multiplan_freeze. Для необязательного параметра plan_type можно задать значение serialized, hintset или template. Значение по умолчанию — serialized. Более подробно типы замороженных планов описаны в Подразделе G.3.3.

Все замороженные планы хранятся в представлении pgpro_multiplan_storage.

G.3.4.4. Пример замороженного плана #

Пример ниже показывает, как использовать замороженные планы.

-- План, который необходимо улучшить
EXPLAIN (ANALYZE, COSTS OFF, TIMING OFF)
SELECT count(*) FROM a
WHERE x = 1 OR (x > 11 AND x < 22) OR x = 22;

                                QUERY PLAN
-------------------------------------------------------------------------
Aggregate (actual rows=1 loops=1)
   ->  Seq Scan on a (actual rows=12 loops=1)
         Filter: ((x = 1) OR ((x > 11) AND (x < 22)) OR (x = 22))
         Rows Removed by Filter: 18
Planning Time: 0.179 ms
Execution Time: 0.069 ms
(6 rows)

-- Установите необходимый режим для pgpro_multiplan
SET pgpro_multiplan.mode = 'frozen';

-- Зарегистрируйте запрос
SELECT sql_hash, const_hash
FROM pgpro_multiplan_register_query('SELECT count(*) FROM a
WHERE x = 1 OR (x > $2 AND x < $1) OR x = $1', 'int', 'int');
       sql_hash       | const_hash
----------------------+------------
 -6037606140259443514 | 2413041345
(1 row)

-- Измените план выполнения запроса
-- Запустите сканирование индекса, отключив последовательное сканирование
SET enable_seqscan = 'off';
EXPLAIN (ANALYZE, COSTS OFF, TIMING OFF)
SELECT count(*) FROM a
WHERE x = 1 OR (x > 11 AND x < 22) OR x = 22;

                                   QUERY PLAN
----------------------------------------------------------------------------
Custom Scan (MultiplanScan) (actual rows=1 loops=1)
    Plan is: tracked
    SQL hash: -6037606140259443514
    Const hash: 2413041345
    Plan hash: 0
    ->  Aggregate (actual rows=1 loops=1)
          ->  Index Only Scan using a_x_idx on a (actual rows=12 loops=1)
                Filter: ((x = 1) OR ((x > $2) AND (x < $1)) OR (x = $1))
                Rows Removed by Filter: 18
                Heap Fetches: 30
Planning Time: 0.235 ms
Execution Time: 0.099 ms
(12 rows)

-- Снова включите последовательное сканирование
RESET enable_seqscan;

-- Заморозьте план выполнения запроса
SELECT pgpro_multiplan_freeze();
 pgpro_multiplan_freeze
 ------------------------
  t
 (1 row)

-- Теперь замороженный план используется со сканированием индекса
EXPLAIN (ANALYZE, COSTS OFF, TIMING OFF)
SELECT count(*) FROM a
WHERE x = 1 OR (x > 11 AND x < 22) OR x = 22;

                                   QUERY PLAN
----------------------------------------------------------------------------
Custom Scan (MultiplanScan) (actual rows=1 loops=1)
  Plan is: frozen, serialized
  SQL hash: -6037606140259443514
  Const hash: 2413041345
  Plan hash: 0
  ->  Aggregate (actual rows=1 loops=1)
        ->  Index Only Scan using a_x_idx on a (actual rows=12 loops=1)
              Filter: ((x = 1) OR ((x > $2) AND (x < $1)) OR (x = $1))
              Rows Removed by Filter: 18
              Heap Fetches: 30
Planning Time: 0.063 ms
Execution Time: 0.119 ms
(12 rows)

G.3.5. Использование базовых планов #

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

Чтобы разрешить использование разрешённых планов, укажите значение baseline в параметре pgpro_multiplan.mode.

Чтобы добавить план, созданный стандартным планировщиком, в набор разрешённых планов, выполните следующие действия:

Для последующих запросов созданный план применяется без изменений, если он есть в наборе разрешённых планов. Если такого плана нет, то используется самый дешёвый план из набора разрешённых.

Примечание

Разрешённые планы могут использоваться только при работающем расширении pg_hint_plan. За подробной информацией обратитесь к разделу Совместимость с другими расширениями. Разрешённые планы не используются, если включён автоматический захват. Не забудьте отключить параметр pgpro_multiplan.auto_capturing после выполнения захвата.

G.3.5.1. Захват плана #

С помощью параметра pgpro_multiplan.auto_capturing можно включить захват всех выполняемых запросов. Получить доступ ко всем захваченным запросам можно с помощью представления pgpro_multiplan_captured_queries.

-- Создайте таблицу 'a'
CREATE TABLE a AS SELECT x, x AS y FROM generate_series(1,1000) x;
CREATE INDEX ON a(x);
CREATE INDEX ON a(y);
ANALYZE;

-- Включите параметр auto_capturing
SET pgpro_multiplan.mode = 'baseline'
SET pgpro_multiplan.auto_capturing = 'on';

-- Выполните запрос
SELECT count(*) FROM a t1, a t2 WHERE t1.x = t2.x AND t1.y <= 1000 AND t2.y > 900;
count
-------
100
(1 row)

-- Выполните запрос ещё раз с другими константами, чтобы получить другой план
SELECT count(*) FROM a t1, a t2 WHERE t1.x = t2.x AND t1.y <= 10 AND t2.y > 900;
count
-------
0
(1 row)

-- Теперь захваченные планы можно увидеть в соответствующем представлении
SELECT * FROM pgpro_multiplan_captured_queries \gx

dbid          | 5
sql_hash      | 6079808577596655075
plan_hash     | -487722818968417375
queryid       | -8984284243102644350
cost          | 36.785
sample_string | SELECT count(*) FROM a t1, a t2 WHERE t1.x = t2.x AND t1.y <= 1000 AND t2.y > 900;
query_string  | SELECT count(*) FROM a t1, a t2 WHERE t1.x = t2.x AND t1.y <= $1 AND t2.y > $2;
constants     | 1000, 900
prep_const    |
hint_str      | Leading(("t1" "t2" )) HashJoin("t1" "t2")  IndexScan("t2" "a_y_idx") SeqScan("t1")
explain_plan  | Aggregate  (cost=36.77..36.78 rows=1 width=8)
              |   Output: count(*)
              |   ->  Hash Join  (cost=11.28..36.52 rows=100 width=0)
              |         Hash Cond: (t1.x = t2.x)
              |         ->  Seq Scan on public.a t1  (cost=0.00..20.50 rows=1000 width=4)
              |               Output: t1.x, t1.y
              |               Filter: (t1.y <= 1000)
              |         ->  Hash  (cost=10.03..10.03 rows=100 width=4)
              |               Output: t2.x
              |               Buckets: 1024  Batches: 1  Memory Usage: 12kB
              |               ->  Index Scan using a_y_idx on public.a t2  (cost=0.28..10.03 rows=100 width=4)
              |                     Output: t2.x
              |                     Index Cond: (t2.y > 900)
              | Query Identifier: -8984284243102644350
              |
-[ RECORD 2 ]-+-----------------------------------------------------------------------------------------------------
dbid          | 5
sql_hash      | 6079808577596655075
plan_hash     | 2719320099967191582
queryid       | -8984284243102644350
cost          | 18.997500000000002
sample_string | SELECT count(*) FROM a t1, a t2 WHERE t1.x = t2.x AND t1.y <= 10 AND t2.y > 900;
query_string  | SELECT count(*) FROM a t1, a t2 WHERE t1.x = t2.x AND t1.y <= $1 AND t2.y > $2;
constants     | 10, 900
prep_const    |
hint_str      | Leading(("t2" "t1" )) HashJoin("t1" "t2")  IndexScan("t2" "a_y_idx") IndexScan("t1" "a_y_idx")
explain_plan  | Aggregate  (cost=18.99..19.00 rows=1 width=8)
              |   Output: count(*)
              |   ->  Hash Join  (cost=8.85..18.98 rows=1 width=0)
              |         Hash Cond: (t2.x = t1.x)
              |         ->  Index Scan using a_y_idx on public.a t2  (cost=0.28..10.03 rows=100 width=4)
              |               Output: t2.x, t2.y
              |               Index Cond: (t2.y > 900)
              |         ->  Hash  (cost=8.45..8.45 rows=10 width=4)
              |               Output: t1.x
              |               Buckets: 1024  Batches: 1  Memory Usage: 9kB
              |               ->  Index Scan using a_y_idx on public.a t1  (cost=0.28..8.45 rows=10 width=4)
              |                     Output: t1.x
              |                     Index Cond: (t1.y <= 10)
              | Query Identifier: -8984284243102644350
              |

-- Отключите автоматический захват. Это не повлияет на планы, которые были захвачены ранее.
SET pgpro_multiplan.auto_capturing = 'off';

G.3.5.2. Одобрение плана #

Чтобы одобрить любой план из представления pgpro_multiplan_captured_queries, используйте функцию pgpro_multiplan_captured_approve() с параметрами dbid, sql_hash и plan_hash.

-- Вручную одобрите план со сканированием по индексам
SELECT pgpro_multiplan_captured_approve(5, 6079808577596655075, 2719320099967191582);

pgpro_multiplan_captured_approve
----------------------------------
t
(1 row)

-- Или одобрите планы, выбранные из списка захваченных планов
SELECT pgpro_multiplan_captured_approve(dbid, sql_hash, plan_hash)
FROM pgpro_multiplan_captured_queries
WHERE query_string like '%SELECT % FROM a t1, a t2%';

pgpro_multiplan_captured_approve
----------------------------------
t
(1 row)

-- Одобренные планы автоматически удаляются из хранилища захваченных планов
SELECT count(*) FROM pgpro_multiplan_captured_queries;

count
-------
0
(1 row)

-- Одобренные планы можно увидеть в представлении pgpro_multiplan_storage
SELECT * FROM pgpro_multiplan_storage \gx
-[ RECORD 1 ]-+------------------------------------------------------------------------------------------------
dbid          | 5
sql_hash      | 6079808577596655075
const_hash    | 0
plan_hash     | -487722818968417375
valid         | t
cost          | 36.785
sample_string | SELECT count(*) FROM a t1, a t2 WHERE t1.x = t2.x AND t1.y <= 1000 AND t2.y > 900;
query_string  | SELECT count(*) FROM a t1, a t2 WHERE t1.x = t2.x AND t1.y <= $1 AND t2.y > $2;
paramtypes    |
query         | <>
plan          | <>
plan_type     | baseline
hintstr       | Leading(("t1" "t2" )) HashJoin("t1" "t2")  IndexScan("t2" "a_y_idx") SeqScan("t1")
wildcards     |
-[ RECORD 2 ]-+------------------------------------------------------------------------------------------------
dbid          | 5
sql_hash      | 6079808577596655075
const_hash    | 0
plan_hash     | 2719320099967191582
valid         | t
cost          | 18.997500000000002
sample_string | SELECT count(*) FROM a t1, a t2 WHERE t1.x = t2.x AND t1.y <= 10 AND t2.y > 900;
query_string  | SELECT count(*) FROM a t1, a t2 WHERE t1.x = t2.x AND t1.y <= $1 AND t2.y > $2;
paramtypes    |
query         | <>
plan          | <>
plan_type     | baseline
hintstr       | Leading(("t2" "t1" )) HashJoin("t1" "t2")  IndexScan("t2" "a_y_idx") IndexScan("t1" "a_y_idx")
wildcards     |

G.3.5.3. Пример разрешённого плана #

Ниже показан пример использования разрешённых планов.

-- Включите параметр auto_capturing
SET pgpro_multiplan.mode = 'baseline'
SET pgpro_multiplan.auto_capturing = 'on';

-- Выполните запрос
EXPLAIN (ANALYZE, COSTS OFF, TIMING OFF)
SELECT count(*) FROM a t1, a t2 WHERE t1.x = t2.x AND t1.y <= 1000 AND t2.y > 900;

                                  QUERY PLAN
--------------------------------------------------------------------------------
Aggregate (actual rows=1 loops=1)
->  Hash Join (actual rows=100 loops=1)
      Hash Cond: (t1.x = t2.x)
      ->  Seq Scan on a t1 (actual rows=1000 loops=1)
            Filter: (y <= 1000)
      ->  Hash (actual rows=100 loops=1)
            Buckets: 1024  Batches: 1  Memory Usage: 12kB
            ->  Index Scan using a_y_idx on a t2 (actual rows=100 loops=1)
                  Index Cond: (y > 900)
Planning Time: 0.543 ms
Execution Time: 0.688 ms
(16 rows)

-- Затем выполните запрос снова с другими константами
EXPLAIN (ANALYZE, COSTS OFF, TIMING OFF)
SELECT count(*) FROM a t1, a t2 WHERE t1.x = t2.x AND t1.y <= 10 AND t2.y > 900;

                                  QUERY PLAN
--------------------------------------------------------------------------------
Aggregate (actual rows=1 loops=1)
->  Hash Join (actual rows=0 loops=1)
      Hash Cond: (t2.x = t1.x)
      ->  Index Scan using a_y_idx on a t2 (actual rows=100 loops=1)
            Index Cond: (y > 900)
      ->  Hash (actual rows=10 loops=1)
            Buckets: 1024  Batches: 1  Memory Usage: 9kB
            ->  Index Scan using a_y_idx on a t1 (actual rows=10 loops=1)
                  Index Cond: (y <= 10)
Planning Time: 0.495 ms
Execution Time: 0.252 ms
(16 rows)

-- Отключите автоматический захват
SET pgpro_multiplan.auto_capturing = 'off';

-- Одобрите все захваченные планы
SELECT pgpro_multiplan_captured_approve(dbid, sql_hash, plan_hash)
FROM pgpro_multiplan_captured_queries;

pgpro_multiplan_captured_approve
----------------------------------
t
t
(2 rows)

-- План не меняется, поскольку он входит в число разрешённых
EXPLAIN (ANALYZE, COSTS OFF, TIMING OFF)
SELECT count(*) FROM a t1, a t2 WHERE t1.x = t2.x AND t1.y <= 1000 AND t2.y > 900;

                                    QUERY PLAN
--------------------------------------------------------------------------------
Custom Scan (MultiplanScan) (actual rows=1 loops=1)
  Plan is: baseline
  SQL hash: 6079808577596655075
  Const hash: 0
  Plan hash: -487722818968417375
  ->  Aggregate (actual rows=1 loops=1)
        ->  Hash Join (actual rows=100 loops=1)
              Hash Cond: (t1.x = t2.x)
        ->  Seq Scan on a t1 (actual rows=1000 loops=1)
              Filter: (y <= 1000)
                       ->  Hash (actual rows=100 loops=1)
                             Buckets: 1024  Batches: 1  Memory Usage: 12kB
                             ->  Index Scan using a_y_idx on a t2 (actual rows=100 loops=1)
                                   Index Cond: (y > 900)
Planning Time: 0.426 ms
Execution Time: 0.519 ms
(16 rows)

-- В обычной ситуации в этом плане выполнялось бы последовательное сканирование для обеих таблиц, но сейчас применяется самый дешёвый план из набора разрешённых
EXPLAIN (ANALYZE, COSTS OFF, TIMING OFF)
SELECT count(*) FROM a t1, a t2 WHERE t1.x = t2.x AND t1.y <= 1000 AND t2.y > 0;

                                    QUERY PLAN
--------------------------------------------------------------------------------
Custom Scan (MultiplanScan) (actual rows=1 loops=1)
  Plan is: baseline
  SQL hash: 6079808577596655075
  Const hash: 0
  Plan hash: 2719320099967191582
  ->  Aggregate (actual rows=1 loops=1)
        ->  Hash Join (actual rows=1000 loops=1)
              Hash Cond: (t2.x = t1.x)
              ->  Index Scan using a_y_idx on a t2 (actual rows=1000 loops=1)
                    Index Cond: (y > $2)
              ->  Hash (actual rows=1000 loops=1)
                    Buckets: 1024  Batches: 1  Memory Usage: 44kB
                    ->  Index Scan using a_y_idx on a t1 (actual rows=1000 loops=1)
                          Index Cond: (y <= $1)
Planning Time: 2.473 ms
Execution Time: 1.859 ms
(16 rows)

G.3.6. Резервные копии замороженных планов #

Расширение pgpro_multiplan позволяет создавать резервные копии замороженных планов и затем восстанавливать эти планы в текущую базу данных. Это может быть полезно для переноса планов между базами данных или экземплярами серверов.

Чтобы создать копию замороженных планов, используйте представление pgpro_multiplan_storage следующим образом:

CREATE TABLE storage_copy AS SELECT s.*
FROM pgpro_multiplan_storage s
JOIN pg_database d ON s.dbid = d.oid
WHERE d.datname = 'db_name';

Чтобы восстановить замороженные планы из резервной копии, вызовите функцию pgpro_multiplan_restore():

SELECT s.query_string, res.sql_hash IS NOT NULL AS success
FROM storage_copy s,
LATERAL pgpro_multiplan_restore(s.query_string, s.hintstr, s.paramtypes, s.plan_type) res;

Примечание

Планы могут быть восстановлены только при работающем расширении pg_hint_plan, см. раздел Совместимость с другими расширениями.

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

G.3.6.1. Ограничения #

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

  • Можно восстановить только замороженные планы.

  • Нельзя восстановить замороженные планы, имеющие тип template. Поддерживаются только планы типов serialized и hintset.

  • Если вы создаёте копию планов из нескольких баз данных, и эти базы данных содержат разные замороженные планы для одинаковых запросов, только первый конфликтующий план будет восстановлен.

  • Можно восстановить только планы для допустимых запросов, что означает, что все отношения, используемые в запросе, должны существовать в текущей базе данных.

G.3.6.2. Сценарии использования #

Этот раздел описывает как создать копии замороженных планов и восстановить их в разных популярных сценариях.

G.3.6.2.1. Обновление версии сервера #

Выполните шаги ниже, чтобы сохранить замороженные планы при обновлении сервера со старой версии с несовместимым хранилищем данных.

  1. Создайте копию замороженных планов до обновления.

    CREATE TABLE storage_copy AS SELECT s.*
    FROM pgpro_multiplan_storage s
    JOIN pg_database d ON s.dbid = d.oid
    WHERE d.datname = 'db_name';
  2. Обновите версию сервера.

  3. Восстановите замороженные планы.

    SELECT pgpro_multiplan_restore(query_string, hintstr, paramtypes, plan_type)
    FROM storage_copy;
G.3.6.2.2. Перенос планов между экземплярами серверов #

Чтобы перенести замороженные планы между двумя серверами, выполните следующее:

  1. Подключитесь к исходному серверу.

  2. Создайте копию замороженных планов в таблицу.

      CREATE TABLE storage_copy AS SELECT s.*
      FROM pgpro_multiplan_storage s
      JOIN pg_database d ON s.dbid = d.oid
      WHERE d.datname = 'db_name';
      
  3. Используйте утилиту pg_dump, чтобы выгрузить таблицу в файл.

    $ pg_dump --table storage_copy -Ft postgres > storage_copy.tar
  4. Подключитесь к целевому серверу и нужной базе данных.

  5. Переместите созданный файл выгрузки в целевую файловую систему.

  6. Используйте утилиту pg_restore, чтобы восстановить таблицу с замороженными планами из файла выгрузки.

    $ pg_restore --dbname postgres -Ft storage_copy.tar
  7. Восстановите замороженные планы.

    SELECT pgpro_multiplan_restore(query_string, hintstr, paramtypes, plan_type)
    FROM storage_copy;
    
    DROP TABLE storage_copy;
G.3.6.2.3. Перенос планов из "песочницы" в обычное хранилище #

Чтобы перенести замороженные планы из "песочницы" в обычное хранилище, выполните следующие шаги:

  1. Установите параметру pgpro_multiplan.sandbox значение on и создайте копию замороженных планов из "песочницы".

    SET pgpro_multiplan.sandbox = ON;
    
    CREATE TABLE storage_copy AS SELECT s.*
    FROM pgpro_multiplan_storage s
    JOIN pg_database d ON s.dbid = d.oid
    WHERE d.datname = 'db_name';
  2. Установите для параметра pgpro_multiplan.sandbox значение off и восстановите замороженные планы в обычное хранилище.

    SET pgpro_multiplan.sandbox = OFF;
    
    SELECT pgpro_multiplan_restore(query_string, hintstr, paramtypes, plan_type)
    FROM storage_copy;
G.3.6.2.4. Перенос планов между базами данных #

Чтобы перенести замороженные планы из одной базы данных в другую, подключитесь к целевой базе данных и восстановите планы, как показано ниже.

SELECT pgpro_multiplan_restore(s.query_string, s.hintstr, s.paramtypes, s.plan_type)
FROM pgpro_multiplan_storage s JOIN pg_database d ON s.dbid = d.oid
WHERE d.datname = 'db_name';

Здесь db_name — это имя базы данных, из которой вы хотите перенести планы.

G.3.6.2.5. Пример переноса планов #

Этот пример показывает, как перенести планы с одного экземпляра сервера на другой.

-- Подключитесь к исходному серверу
psql (17.4)
Type "help" for help.

-- В этом примере 1000 планов хранится в представлении pgpro_multiplan_storage
postgres=# select count(*) from pgpro_multiplan_storage;
  count
-------
  1000
(1 row)

-- Скопируйте замороженные планы из базы данных postgres в таблицу
postgres=# CREATE TABLE storage_copy AS SELECT s.*
FROM pgpro_multiplan_storage s
JOIN pg_database d ON s.dbid = d.oid
WHERE d.datname = 'postgres';

-- Выгрузите таблицу в файл архива
$ pg_dump --table storage_copy -Ft postgres > storage_copy.tar

-- Отключитесь от исходного сервера
-- Подключитесь к целевому серверу
./psql postgres
psql (16.8)
Type "help" for help.

-- Создайте расширение pgpro_multiplan и включите его
postgres=# create extension pgpro_multiplan;
CREATE EXTENSION
SET pgpro_multiplan.mode = 'frozen';
SET

-- Этот сервер не содержит замороженных планов
postgres=# select count(*) from pgpro_multiplan_storage;
 count
-------
     0
(1 row)

-- Переместите файл выгрузки с замороженными планами в целевую файловую систему

-- Восстановите таблицу с замороженными планами из файла выгрузки
$ pg_restore --dbname postgres -Ft storage_copy.tar

-- Восстановите замороженные планы из таблицы с помощью функции pgpro_multiplan_restore
postgres=# SELECT pgpro_multiplan_restore(query_string, hintstr, paramtypes, plan_type)
FROM storage_copy;
     pgpro_multiplan_restore
----------------------------------
 (8436876698844323073,871432885)
 (8436876698844323073,573678316)
 (8436876698844323073,1999378082)
 (8436876698844323073,1681603536)
 (8436876698844323073,3959620774)
...
 (8436876698844323073,1263226437)
 (8436876698844323073,4053700861)
 (8436876698844323073,2418458596)
 (8436876698844323073,413896030)
(1000 rows)

-- Функция восстановила 1000 замороженных планов. Результат показан в виде пар sql_hash и const_hash
-- Замороженные запросы были однотипными и отличались только константами, поэтому sql_hash одинаковый для всех планов

-- Удалите таблицу, используемую для восстановления планов
postgres=# DROP TABLE storage_copy;
DROP TABLE

-- Целевой сервер теперь тоже хранит 1000 замороженных планов
postgres=# select count(*) from pgpro_multiplan_storage;
 count
-------
  1000
(1 row)

-- Выключите pgpro_multiplan и выполните запрос
SET pgpro_multiplan.mode = '';

SET
postgres=# EXPLAIN (COSTS OFF) SELECT * FROM a WHERE x > 10;
     QUERY PLAN
--------------------
 Seq Scan on a
   Filter: (x > 10)
(2 rows)

-- Включите pgpro_multiplan и выполните тот же запрос ещё раз
-- Теперь используется один из восстановленных планов
SET pgpro_multiplan.mode = 'frozen';
SET
postgres=# EXPLAIN (COSTS OFF) SELECT * FROM a WHERE x > 10;
             QUERY PLAN
-------------------------------------
 Custom Scan (MultiplanScan)
   Plan is: frozen, serialized
   SQL hash: 8436876698844323073
   Const hash: 2295408638
   Plan hash: 0
   ->  Index Scan using a_x_idx on a
         Index Cond: (x > 10)
(7 rows)

G.3.7. Совместимость с другими расширениями #

Для обеспечения совместимости pgpro_multiplan с другими расширениями необходимо в файле postgresql.conf в переменной shared_preload_libraries указать имена библиотек в определённом порядке:

  • pg_hint_plan: расширение pgpro_multiplan необходимо загрузить после pg_hint_plan.

    shared_preload_libraries = 'pg_hint_plan, pgpro_multiplan'
  • aqo: расширение pgpro_multiplan необходимо загружать до aqo.

    shared_preload_libraries = 'pgpro_multiplan, aqo'
  • pgpro_stats: расширение pgpro_multiplan необходимо загружать после pgpro_stats.

    shared_preload_libraries = 'pgpro_stats, pgpro_multiplan'

G.3.8. Идентификация запросов #

Запрос в текущей базе данных идентифицируется уникальной парой sql_hash и const_hash.

sql_hash — это хеш, сформированный на основе дерева разбора без учёта параметров и констант. Псевдонимы полей и таблиц не игнорируются, поэтому один и тот же запрос с разными псевдонимами будет иметь разные значения sql_hash.

const_hash — хеш, сгенерированный на основе всех присутствующих в запросе констант. Константы с одинаковым значением, но разным типом, например 1 и '1', выдадут разное значение хеша.

G.3.9. Автоматическое приведение типов #

pgpro_multiplan пытается автоматически приводить типы констант из запроса к типам параметров запроса, для которого план был заморожен или добавлен в список разрешённых. Если привести типы невозможно, план игнорируется.

SELECT sql_hash, const_hash
FROM pgpro_multiplan_register_query('SELECT count(*) FROM a
WHERE x = $1', 'int');

-- Приведение типов возможно
EXPLAIN SELECT count(*) FROM a WHERE x = '1';
                     QUERY PLAN
-------------------------------------------------------------
Custom Scan (MultiplanScan)  (cost=1.38..1.39 rows=1 width=8)
  Plan is: tracked
  SQL hash: -5166001356546372387
  Const hash: 0
  Plan hash: 0
  ->  Aggregate  (cost=1.38..1.39 rows=1 width=8)
        ->  Seq Scan on a  (cost=0.00..1.38 rows=1 width=0)
              Filter: (x = $1)

-- Приведение типов возможно
EXPLAIN SELECT count(*) FROM a WHERE x = 1::bigint;
                     QUERY PLAN
-------------------------------------------------------------
Custom Scan (MultiplanScan)  (cost=1.38..1.39 rows=1 width=8)
  Plan is: tracked
  SQL hash: -5166001356546372387
  Const hash: 0
  Plan hash: 0
  ->  Aggregate  (cost=1.38..1.39 rows=1 width=8)
        ->  Seq Scan on a  (cost=0.00..1.38 rows=1 width=0)
              Filter: (x = $1)

-- Приведение типов невозможно
EXPLAIN SELECT count(*) FROM a WHERE x = 1111111111111;
                  QUERY PLAN
-------------------------------------------------------
 Aggregate  (cost=1.38..1.39 rows=1 width=8)
   ->  Seq Scan on a  (cost=0.00..1.38 rows=1 width=0)
         Filter: (x = '1111111111111'::bigint)

G.3.10. Статистика использования планов #

Чтобы собирать статистику об использовании замороженных и разрешённых планов, укажите значение statistics в параметре pgpro_multiplan.mode. Эта статистика хранится в представлении pgpro_multiplan_stats. Параметр pgpro_multiplan.max_stats задаёт максимальное количество собираемых статистических значений. При достижении этого ограничения дальнейшая статистика будет игнорироваться. Если план изменяется, статистика использования этого плана сбрасывается и пересчитывается с новым plan_hash.

Для получения более детальной статистики планирования и выполнения запросов можно использовать расширение pgpro_stats (см. секцию Совместимость с другими расширениями). Доступ к этой статистике можно получить с помощью представления pgpro_stats_statements.

Вы можете объединить информацию из представлений pgpro_multiplan_stats и pgpro_stats_statements по полю planid.

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

-- Включите сбор статистики
SET pgpro_multiplan.mode = 'frozen, statistics';

-- Выполните запрос
SELECT count(*) FROM a
WHERE x = 1 OR (x > 11 AND x < 22) OR x = 22;

 count
-------
    12
(1 row)

-- Теперь можно посмотреть статистику использования плана
SELECT * FROM pgpro_multiplan_stats;

 dbid |      sql_hash       | const_hash | plan_hash |       planid        | counter
------+---------------------+------------+-----------+---------------------+---------
    5 | 6062491547151210914 | 2413041345 |         0 | 3549961214127427294 |       1
(1 row)

G.3.11. Интеграция с AQE #

Расширение pgpro_multiplan может работать совместно с адаптивным выполнением запросов (adaptive query execution, AQE), предоставляя более гибкие возможности для управления планами выполнения запросов.

AQE пытается переоптимизировать запрос, если во время выполнения запроса срабатывает определённый триггер, указывающий на неоптимальность плана. Чтобы включить AQE, используйте параметр конфигурации aqe_enable.

Параметр конфигурации pgpro_multiplan.aqe_mode задаёт функции, связанные с AQE, в виде списка, разделённого запятыми.

G.3.11.1. Управление планами в реальном времени #

При управлении планами в реальном времени pgpro_multiplan автоматически добавляет планы, созданные с помощью AQE, в список разрешённых планов.

Чтобы включить эту функциональность, выполните следующие шаги:

В следующем примере показано, как использовать управление планами в реальном времени.

INSERT INTO a SELECT x, x AS y FROM generate_series(1001,2000) x;

-- Включите AQE и триггер количества обработанных кортежей узлов
SET aqe_enable = 'on';
SET aqe_rows_underestimation_rate_trigger = 2;

-- Настройте режимы pgpro_multiplan
SET pgpro_multiplan.mode = 'baseline';
SET pgpro_multiplan.aqe_mode = 'auto_approve_plans';

-- Выполните запрос
SELECT count(*) FROM a t1, a t2 WHERE t1.x = t2.x AND t1.y <= 2000 AND t2.y > 900;
  count
-------
  1100
(1 row)

-- Запрос был переоптимизирован с помощь AQE в соответствии с триггером
-- План выполнения запроса был сохранён в список базовых (разрешённых) планов

-- Теперь можно увидеть этот план в соответствующем представлении
SELECT * FROM pgpro_multiplan_storage \gx
-[ RECORD 1 ]-+------------------------------------------------------------------------------------
dbid          | 16384
sql_hash      | 771426262442742808
const_hash    | 0
plan_hash     | -487722818968417375
valid         | t
cost          | 75.78750000000001
sample_string | SELECT count(*) FROM a t1, a t2 WHERE t1.x = t2.x AND t1.y <= 2000 AND t2.y > 900;
query_string  | SELECT count(*) FROM a t1, a t2 WHERE t1.x = t2.x AND t1.y <= $1 AND t2.y > $2;
paramtypes    |
query         | <>
plan_type     | baseline
plan          | <>
hintstr       | Leading(("t1" "t2" )) HashJoin("t1" "t2")  IndexScan("t2" "a_y_idx") SeqScan("t1")
wildcards     |

-- Выключите AQE
RESET aqe_enable;
RESET aqe_rows_underestimation_rate_trigger;

-- Выполните тот же запрос и посмотрите, что используется ранее сохранённый план
explain analyze
SELECT count(*) FROM a t1, a t2 WHERE t1.x = t2.x AND t1.y <= 2000 AND t2.y > 900;
                                                                 QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------------------
 Custom Scan (MultiplanScan)  (cost=52.03..52.04 rows=1 width=8) (actual time=2.148..2.150 rows=1 loops=1)
   Plan is: baseline
   SQL hash: 771426262442742808
   Const hash: 0
   Plan hash: -487722818968417375
   ->  Aggregate  (cost=52.03..52.04 rows=1 width=8) (actual time=2.148..2.149 rows=1 loops=1)
         ->  Hash Join  (cost=13.78..51.65 rows=150 width=0) (actual time=1.390..2.051 rows=1100 loops=1)
               Hash Cond: (t1.x = t2.x)
               ->  Seq Scan on a t1  (cost=0.00..30.75 rows=1500 width=4) (actual time=0.028..0.597 rows=2000 loops=1)
                     Filter: (y <= 2000)
               ->  Hash  (cost=11.90..11.90 rows=150 width=4) (actual time=0.993..0.993 rows=1100 loops=1)
                     Buckets: 2048 (originally 1024)  Batches: 1 (originally 1)  Memory Usage: 55kB
                     ->  Index Scan using a_y_idx on a t2  (cost=0.28..11.90 rows=150 width=4) (actual time=0.026..0.695 rows=1100 loops=1)
                           Index Cond: (y > 900)
 Planning Time: 0.460 ms
 Execution Time: 2.194 ms
(16 rows)

G.3.11.2. Индивидуальные значения триггеров #

Расширение pgpro_multiplan разрешает переопределять и настраивать глобальные значения триггеров AQE> для отдельных запросов. Глобальные триггеры указываются в параметрах конфигурации aqe_sql_execution_time_trigger, aqe_rows_underestimation_rate_trigger и aqe_backend_memory_used_trigger.

Чтобы использовать эту функциональность, выполните следующие шаги:

  • Установите для параметра aqe_enable значение on, чтобы активировать AQE.

  • Укажите значение individual_triggers в параметре pgpro_multiplan.aqe_mode.

  • Вызовите функцию set_aqe_trigger для каждого запроса, триггеры которого нужно переопределить.

Все индивидуальные значения триггеров показаны в представлении aqe_triggers. Параметр pgpro_multiplan.aqe_max_items указывает максимальное количество хранимых значений триггеров.

G.3.11.3. Статистика AQE #

pgpro_multiplan может собирать статистику AQE для всех выражений, которые рассматриваются для переоптимизации. Эта статистика хранится в разделяемой памяти до выключения сервера и не реплицируется.

Выполните следующие шаги для включения сбора статистики:

  • Установите для параметра aqe_enable значение on, чтобы активировать AQE.

  • Укажите значение statistics в параметре pgpro_multiplan.aqe_mode.

  • Включите вычисление идентификаторов запросов с помощью параметра compute_query_id.

Все статистические значения показаны в представлении aqe_stats. Параметр pgpro_multiplan.aqe_max_stats указывает максимальное количество хранимых статистических значений. Дальнейшие статистические значения игнорируются.

G.3.12. Глобальные подготовленные операторы #

По умолчанию подготовленные операторы доступны только внутри текущего сеанса базы данных. Доступ к этим операторам можно получить с помощью системного представления pg_prepared_statements. При завершении сеанса подготовленные операторы освобождаются и затем не могут быть использованы повторно.

Расширение pgpro_multiplan поддерживает глобальные подготовленные операторы, которые доступны нескольким или всем сеансам в рамках текущего сервера. Чтобы включить эту функциональность, установите для параметра pgpro_multiplan.global_prepared_statements значение on одним из следующих способов:

  • при запуске сервера (в файле postgresql.conf), чтобы активировать функциональность для всех сеансов

  • для отдельных необходимых сеансов с помощью команды SET

Когда подготовленный оператор создаётся в таком сеансе, он сохраняется в глобальном хранилище и затем может быть использован параллельно во всех активных сеансах. Доступ к этим глобальным подготовленным операторам можно получить с помощью представления pgpro_global_prepared_statements. При завершении сеанса подготовленные операторы, созданные в нём, не удаляются и остаются доступными для других сеансов. Но если подготовленный оператор освобождается явным образом, он удаляется из глобального хранилища.

Пример ниже показывает, как использовать глобальные подготовленные операторы в двух сеансах.

-- Откройте первый сеанс и создайте подготовленный оператор с именем "p"
PREPARE p (int,int) as SELECT $1+$2 as plus;

-- Выполните созданный подготовленный оператор в том же сеансе
EXECUTE p (1,2);
plus
------
    3
(1 row)

-- Откройте второй сеанс и попробуйте создать подготовленный оператор с таким же именем
-- Возникает ошибка, поскольку этот оператор уже существует
PREPARE p (int,int) as SELECT $1+$2 as plus;
ERROR:  shared prepared statement "p" already exists

-- Выполните подготовленный оператор во втором сеансе
EXECUTE p (1,2);
plus
------
    3
(1 row)

-- Освободите подготовленный оператор во втором сеансе
DEALLOCATE p;

-- Попробуйте выполнить этот оператор в первом сеансе
-- Возникает ошибка, поскольку этот оператор был освобождён во втором сеансе
EXECUTE p (1,2);
ERROR:  prepared statement "p" does not exist

Можно также установить для параметра pgpro_multiplan.global_prepared_statements_allow_duplicate значение on, чтобы не показывать ошибки при попытках создать подготовленные операторы, которые уже существуют, как в примере выше. Однако при создании подготовленного оператора с таким же именем, но с другим запросом, ошибка всё равно возникнет.

G.3.12.1. Особенности и ограничения #

Учитывайте следующие особенности и ограничения при использовании глобальных подготовленных операторов:

  • Глобальные подготовленные операторы доступны только в рамках одного сервера и не могут использоваться несколькими серверами в кластере.

  • Глобальное хранилище содержит подготовленные операторы, созданные в сеансе только после установки для параметра pgpro_multiplan.global_prepared_statements значения on. Даже если подготовленный оператор был создан раньше этого момента и доступен в локальном системном представлении pg_prepared_statements, он не добавляется в представление pgpro_global_prepared_statements. При попытке выполнить такой оператор возникнет ошибка и оператор удалится из локального хранилища.

  • Для корректного выполнения подготовленного оператора все отношения, задействованные в нём, должны существовать во всех сеансах. Таким образом, подготовленные операторы не должны использовать временные таблицы или любые другие объекты, которые не поддерживаются репликацией WAL.

G.3.13. Представления #

G.3.13.1. Представление pgpro_multiplan_storage #

Представление pgpro_multiplan_storage содержит подробную информацию обо всех замороженных и разрешённых планах. Столбцы представления показаны в Таблице G.4.

Таблица G.4. Столбцы pgpro_multiplan_storage

ИмяТипОписание
dbidoidИдентификатор базы данных, в которой выполнялся оператор
sql_hashbigintВнутренний идентификатор запроса
const_hashbigintХеш непараметризованных констант
plan_hashbigintВнутренний идентификатор разрешённого плана, 0 — для замороженных планов
validbooleanFALSE, если план был аннулирован при последнем использовании
costfloatСтоимость разрешённого плана, 0 — для замороженных планов
sample_stringtextНепараметризованный запрос с константами, для которого план был заморожен или добавлен в список разрешённых
query_stringtextПараметризованный запрос, для которого план был заморожен или добавлен в список разрешённых
paramtypesregtype[]Массив с типами параметров, использованными в запросе
querytextВнутреннее представление запроса
plantextВнутреннее представление плана
plan_typetextТип плана. Для замороженных планов: serialized, hintset или template. Для разрешённых планов: baseline
hintstrtextНабор указаний, сформированный на основе плана
wildcardstextРегулярное выражение, используемое для замороженного плана типа template, NULL для остальных типов планов

G.3.13.2. Представление pgpro_multiplan_local_cache #

Представление pgpro_multiplan_local_cache содержит подробную информацию о зарегистрированных и замороженных операторах в локальном кеше. Столбцы представления показаны в Таблице G.5.

Таблица G.5. Столбцы pgpro_multiplan_local_cache

ИмяТипОписание
sql_hashbigintВнутренний идентификатор запроса
const_hashbigintХеш непараметризованных констант
fs_is_frozenbooleanTRUE, если оператор был заморожен
fs_is_validbooleanTRUE, если оператор действителен
ps_is_validbooleanTRUE, если оператор должен быть перепроверен
query_stringtextЗапрос, зарегистрированный функцией pgpro_multiplan_register_query
querytextВнутреннее представление запроса
paramtypesregtype[]Массив с типами параметров, использованными в запросе
hintstrtextНабор указаний, сформированный на основе замороженного плана

G.3.13.3. Представление pgpro_multiplan_captured_queries #

Представление pgpro_multiplan_captured_queries содержит подробную информацию обо всех запросах, отслеживаемых в сеансах. Столбцы представления показаны в Таблице G.6.

Таблица G.6. Столбцы pgpro_multiplan_captured_queries

ИмяТипОписание
dbidoidИдентификатор базы данных, в которой выполнялся оператор
sql_hashbigintВнутренний идентификатор запроса
queryidbigintСтандартный идентификатор запроса
plan_hashbigintВнутренний идентификатор плана
planidbigintИдентификатор плана, совместимый с расширением pgpro_stats
costfloatСтоимость плана
sample_stringtextПоследний использованный непараметризованный запрос с константами
query_stringtextПоследний использованный параметризованный запрос
constantstextНабор констант в запросе
prep_conststextНабор констант, использованных для выполнения (EXECUTE) подготовленного оператора
hintstrtextНабор указаний, сформированный на основе плана
explain_plantextПлан, показанный командой EXPLAIN

G.3.13.4. Представление pgpro_multiplan_stats #

Представление pgpro_multiplan_stats предоставляет статистику использования замороженных и разрешённых планов. Столбцы представления показаны в Таблице G.7.

Таблица G.7. Столбцы pgpro_multiplan_stats

ИмяТипОписание
dbidoidИдентификатор базы данных, в которой выполнялся оператор
sql_hashbigintВнутренний идентификатор запроса
plan_hashbigintВнутренний идентификатор плана
planidbigintИдентификатор плана, совместимый с расширением pgpro_stats
counterbigintКоличество использований плана

G.3.13.5. Представление aqe_triggers #

Представление aqe_triggers содержит информацию об индивидуальных значениях триггеров AQE. Столбцы представления показаны в Таблице G.8.

Таблица G.8. Столбцы aqe_triggers

ИмяТипОписание
dbidoidИдентификатор базы данных, в которой выполнялся оператор
sql_hashbigintВнутренний идентификатор запроса
query_stringtextПараметризованный запрос
execution_timeintЗначение для триггера времени выполнения запроса, в миллисекундах. NULL, если используется глобальное значение триггера
memoryintЗначение для триггера потребления памяти рабочим процессом. NULL, если используется глобальное значение триггера
underestimation_ratedoubleКоэффициент для триггера количества обработанных кортежей узлов. NULL, если используется глобальное значение триггера

G.3.13.6. Представление aqe_stats #

Представление aqe_stats содержит сводную статистику о переоптимизациях AQE. Это представление содержит одну строку для каждой комбинации идентификатора базы данных, запроса и плана выполнения. Столбцы представления показаны в Таблице G.9.

Таблица G.9. Столбцы aqe_stats

ИмяТипОписание
dbidoidИдентификатор базы данных, в которой выполнялся оператор
sql_hashbigintВнутренний идентификатор запроса
planidbigintИдентификатор плана выполнения запроса
querytextВнутреннее представление запроса
last_updatedtimestamp with time zoneВремя последнего обновления статистики
exec_numbigintКоличество выполнений запроса
min_attemptsintegerМинимальное количество переоптимизаций запроса
max_attemptsintegerМаксимальное количество переоптимизаций запроса
total_attemptsintegerОбщее количество переоптимизаций запроса
reason_repeated_planbigintКоличество отключений AQE из-за генерации повторного плана выполнения
reason_no_databigintКоличество отключений AQE из-за отсутствия новой информации, полученной во время выполнения
reason_max_rerunsbigintКоличество отключений AQE из-за достижения максимального количества перезапусков
reason_externalbigintКоличество раз, когда AQE было отключено расширением, например, pgpro_multiplan
reruns_forcedbigintОбщее количество переоптимизаций, вызванных ручным триггером
reruns_timebigintОбщее количество переоптимизаций, вызванных триггером времени выполнения запроса
reruns_underestimationbigintОбщее количество переоптимизаций, вызванных триггером количества обработанных кортежей узлов
reruns_memorybigintОбщее количество переоптимизаций, вызванных триггером потребления памяти рабочим процессом
min_planning_timedouble precisionМинимальное время, затраченное на планирование, в миллисекундах
max_planning_timedouble precisionМаксимальное время, затраченное на планирование, в миллисекундах
mean_planning_timedouble precisionСреднее время, затраченное на планирование, в миллисекундах
stddev_planning_timedouble precisionСтандартное отклонение времени, затраченного на планирование, в миллисекундах
min_exec_timedouble precisionМинимальное время, затраченное на выполнение запроса, в миллисекундах
max_exec_timedouble precisionМаксимальное время, затраченное на выполнение запроса, в миллисекундах
mean_exec_timedouble precisionСреднее время, затраченное на выполнение запроса, в миллисекундах
stddev_exec_timedouble precisionСтандартное отклонение времени, затраченного на выполнение запроса, в миллисекундах

G.3.13.7. Представление pgpro_global_prepared_statements #

Представление pgpro_global_prepared_statements содержит подробную информацию обо всех глобальных подготовленных операторах. Столбцы представления показаны в Таблице G.10.

Таблица G.10. Столбцы pgpro_global_prepared_statements

ИмяТипОписание
session_idbyteaИдентификатор сеанса, в котором был создан подготовленный оператор
namevarcharИмя подготовленного оператора
prepare_timetimestampИзначальное время создания подготовленного оператора. Если тот же подготовленный оператор создаётся повторно, временная отметка не обновляется
from_sqlbooltrue, если подготовленный оператор был создан с помощью команды PREPARE, false в противном случае
param_numint4Количество параметров, используемых в подготовленном операторе
param_typesregtype[]Массив с типами параметров, используемых в подготовленном операторе
statementtextЗапрос, использованный для создания подготовленного оператора

G.3.14. Функции #

Вызывать нижеуказанные функции могут только суперпользователи.

pgpro_multiplan_register_query(query_string text) returns record
pgpro_multiplan_register_query(query_string text, VARIADIC regtype[]) returns record #

Сохраняет запрос, описанный в параметре query_string, в локальном кеше, к которому можно получить доступ с помощью представления pgpro_multiplan_local_cache. Возвращает уникальную пару sql_hash и const_hash.

pgpro_multiplan_unregister_query() returns bool #

Удаляет запрос, который был зарегистрирован, но не был заморожен, из представления pgpro_multiplan_local_cache. Если нет ошибок, возвращает true.

pgpro_multiplan_freeze(plan_type text) returns bool #

Замораживает последний использованный для запроса план и сохраняет его в постоянном хранилище pgpro_multiplan, к которому можно получить доступ с помощью представления pgpro_multiplan_storage. Допустимые значения необязательного аргумента plan_type: serialized, hintset и template. По умолчанию используется serialized. За подробной информацией о типах планов обратитесь к разделу Поддерживаемые режимы и типы планов. Если план был успешно заморожен, возвращает true.

pgpro_multiplan_unfreeze(sql_hash bigint, const_hash bigint) returns bool #

Удаляет указанный план из постоянного хранилища, но оставляет запрос зарегистрированным в локальном кеше. Если нет ошибок, возвращает true.

pgpro_multiplan_remove(sql_hash bigint, const_hash bigint) returns bool #

Удаляет замороженный оператор с указанными параметрами sql_hash и const_hash. Работает как функции pgpro_multiplan_unfreeze и pgpro_multiplan_unregister_query, вызываемые последовательно. Если нет ошибок, возвращает true.

pgpro_multiplan_reset(dbid oid) returns bigint #

Удаляет все записи в представлении pgpro_multiplan_storage для указанной базы данных. Чтобы удалить данные, собранные pgpro_multiplan для текущей базы данных, не указывайте dbid. Чтобы сбросить данные для всех баз данных, установите для параметра dbid значение NULL. Возвращает количество удалённых записей.

pgpro_multiplan_reload_frozen_plancache() returns bool #

Удаляет все замороженные планы и снова загружает их в представление pgpro_multiplan_storage. Также удаляет операторы, которые были зарегистрированы, но не заморожены. Если нет ошибок, возвращает true.

pgpro_multiplan_stats() returns table #

Возвращает статистику использования планов из представления pgpro_multiplan_stats.

pgpro_multiplan_registered_query(sql_hash bigint, const_hash bigint) returns table #

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

pgpro_multiplan_captured_approve(dbid oid, sql_hash bigint, plan_hash bigint) returns bool #

Добавляет указанный план для захваченного запроса в набор базовых (разрешённых) планов и сохраняет его в представлении pgpro_multiplan_storage. Если план был успешно добавлен, возвращает true.

pgpro_multiplan_remove_baseline(dbid oid, sql_hash bigint, plan_hash bigint) returns bool #

Удаляет указанный разрешённый план из постоянного хранилища. Если план был успешно удалён, возвращает true.

pgpro_multiplan_remove_template(dbid oid, sql_hash bigint, plan_hash bigint) returns bool #

Удаляет указанный план типа template из постоянного хранилища. Если план был успешно удалён, возвращает true.

pgpro_multiplan_set_plan_type(sql_hash bigint, const_hash bigint, plan_type text) returns bool #

Устанавливает тип плана запроса для замороженного оператора. Допустимые значения аргумента plan_type: serialized, hintset и template. Чтобы иметь возможность использовать план запроса типа hintset или template, необходимо загрузить модуль pg_hint_plan. Если тип плана был успешно изменён, возвращает true.

pgpro_multiplan_hintset_update(sql_hash bigint, const_hash bigint, hintset text) returns bool #

Заменяет сгенерированный набор указаний для замороженного плана типа hintset или для разрешённого плана на заданный пользовательский набор указаний. Строка с такими пользовательскими указаниями не должна задаваться в комментарии особого вида, используемого в pg_hint_plan, то есть она не должна начинаться с символов /*+ и заканчиваться символами */. Если набор указаний был успешно изменён, возвращает true.

pgpro_multiplan_template_hintset_update(sql_hash bigint, const_hash bigint, hintset text) returns bool #

Заменяет сгенерированный набор указаний для замороженного плана типа template на заданный пользовательский набор указаний. Строка с такими пользовательскими указаниями не должна задаваться в комментарии особого вида, используемого в pg_hint_plan, то есть она не должна начинаться с символов /*+ и заканчиваться символами */. Если набор указаний был успешно изменён, возвращает true.

pgpro_multiplan_captured_clean() returns bigint #

Удаляет все записи из представления pgpro_multiplan_captured_queries. Возвращает количество удалённых записей.

get_sql_hash(query_string text) returns bigint #

Возвращает внутренний идентификатор (sql_hash) для указанного запроса.

set_aqe_trigger(trigger_name text, trigger_val int, query_string text) returns bool
set_aqe_trigger(trigger_name text, trigger_val double precision, query_string text) returns bool #

Задаёт или сбрасывает индивидуальное значение триггера AQE для указанного запроса. Индивидуальное значение триггера переопределяет глобальное значение триггера, указанное в параметре конфигурации AQE. Чтобы эта функциональность работала, значение individual_triggers должно быть указано в параметре pgpro_multiplan.aqe_mode.

Эта функция имеет следующие аргументы:

  • trigger_name: имя триггера. Разрешены следующие значения:

    • execution_time: триггер времени выполнения запроса. Соответствует параметру конфигурации aqe_sql_execution_time_trigger.

    • memory: триггер потребления памяти рабочим процессом. Соответствует параметру конфигурации aqe_backend_memory_used_trigger.

    • underestimation_rate: триггер количества обработанных кортежей узлов. Соответствует параметру конфигурации aqe_rows_underestimation_rate_trigger.

  • trigger_val: значение триггера. Для триггеров execution_time и memory указывайте целочисленные значения. Для underestimation_rate можно указать значения с двойной точностью. Чтобы сбросить индивидуальное значение триггера, передайте в качестве значения NULL или отрицательное число меньше -1.

  • query_string: текст запроса.

Если нет ошибок, эта функция возвращает true.

aqe_triggers_reset(dbid oid) returns bigint #

Удаляет все записи из представления aqe_triggers для указанной базы данных. Чтобы очистить представление aqe_triggers для текущей базы данных, не указывайте dbid. Чтобы удалить записи из представления для всех баз данных, установите для параметра dbid значение NULL. Возвращает количество удалённых записей.

aqe_stats_reset(dbid oid) returns bigint #

Удаляет все записи из представления aqe_stats для указанной базы данных. Чтобы очистить представление aqe_stats для текущей базы данных, не указывайте dbid. Чтобы удалить записи из представления для всех баз данных, установите для параметра dbid значение NULL. Возвращает количество удалённых записей.

pgpro_multiplan_restore(query_string text, hintstr texttext, paramtypes regtype[], plan_type text) returns record #

Восстанавливает замороженный план для указанного запроса в текущую базу данных.

Эта функция имеет следующие аргументы:

  • query_string: запрос с параметрами $n (аналогично PREPARE statement_name AS), для которого восстанавливается замороженный план на основе набора указаний.

  • hintstr: набор указаний, поддерживаемых расширением pg_hint_plan. Если для аргумента указано значение NULL или пустая строка, будет использоваться стандартный план.

  • parameter_type: массив с типами параметров, используемых в запросе. Если для аргумента указано значение NULL, типы параметров должны быть определены автоматически.

  • plan_type: тип плана. Допустимые значения: serialized и hintset. План с типом template не поддерживается.

Функция возвращает уникальную пару sql_hash и const_hash, если план был успешно восстановлен. В противном случае, она возвращает NULL.

pgpro_global_prepared_statements_reset() returns int #

Удаляет все глобальные подготовленные операторы и возвращает количество удалённых записей.

pgpro_global_prepared_statements_reset(session_id bytea) returns int #

Удаляет все подготовленные операторы, созданные в указанном сеансе, и возвращает количество удалённых записей.

pgpro_global_prepared_statements_session() returns bytea #

Возвращает идентификатор текущего сеанса.

pgpro_global_prepared_statements_session(session_id bytea) #

Устанавливает указанный идентификатор для текущего сеанса и возвращает предыдущий идентификатор.

G.3.15. Параметры конфигурации #

pgpro_multiplan.mode (string) #

Список включённых режимов pgpro_multiplan, разделённых запятой. Доступны следующие значения:

  • frozen: разрешает использование замороженных планов с типами serialized и hintset.

  • wildcards: разрешает использование замороженных планов с типом template.

  • baseline: разрешает использование базовых (разрешённых) планов.

  • statistics: разрешает сбор статистики использования планов. Эта статистика хранится в представлении pgpro_multiplan_stats. Это значение параметра может быть указано только совместно с одним или несколькими типами планов.

SET pgpro_multiplan.mode = 'frozen, wildcards, baseline, statistics';

Подробнее о типах планов можно узнать в разделе Поддерживаемые режимы и типы планов. За информацией о сборе статистики обратитесь к разделу Статистика.

По умолчанию для параметра pgpro_multiplan.mode задана пустая строка, означающая, что все режимы планов выключены. Изменить этот параметр могут только суперпользователи.

pgpro_multiplan.aqe_mode (string) #

Включённые функции, связанные с AQE, в виде списка, разделённого запятыми. Доступны следующие значения:

SET pgpro_multiplan.aqe_mode = 'auto_approve_plans, individual_triggers, statistics';

Чтобы эта функциональность работала, AQE должно быть включено с помощью параметра конфигурации aqe_enable.

По умолчанию для параметра pgpro_multiplan.aqe_mode указана пустая строка. Изменить этот параметр могут только суперпользователи.

pgpro_multiplan.max_stats (integer) #

Задаёт максимальное количество статистических значений, которые могут храниться в представлении pgpro_multiplan_stats. Дальнейшая статистика будет игнорироваться. Значение по умолчанию — 5000. Этот параметр можно задать только при запуске сервера.

pgpro_multiplan.max_items (integer) #

Задаёт максимальное количество записей, с которым может работать pgpro_multiplan. Значение по умолчанию — 100. Этот параметр можно задать только при запуске сервера.

pgpro_multiplan.auto_tracking (boolean) #

Позволяет pgpro_multiplan автоматически нормализовать и регистрировать запросы, выполняемые с использованием команды EXPLAIN. Значение по умолчанию — off. Изменить этот параметр могут только суперпользователи.

pgpro_multiplan.max_local_cache_size (integer) #

Задаёт максимальный размер локального кеша, в килобайтах. Значение по умолчанию — 0, что означает отсутствие ограничений. Изменить этот параметр могут только суперпользователи.

pgpro_multiplan.wal_rw (boolean) #

Включает физическую репликацию данных pgpro_multiplan. При значении off на главном сервере данные на резервный сервер не передаются. При значении off на резервном сервере любые данные, передаваемые с главного сервера, игнорируются. Значение по умолчанию — off. Этот параметр можно задать только при запуске сервера.

pgpro_multiplan.auto_capturing (boolean) #

Включает автоматическое отслеживание запросов в pgpro_multiplan. Если для этого параметра конфигурации установить значение on, в представлении pgpro_multiplan_captured_queries можно будет увидеть запросы с константами в текстовой форме и параметризованные запросы. Также будут видны все планы для каждого запроса. Информация о выполненных запросах хранится до перезапуска сервера. Значение по умолчанию — off. Изменить этот параметр могут только суперпользователи.

pgpro_multiplan.max_captured_items (integer) #

Задаёт максимальное количество запросов, которые может отслеживать pgpro_multiplan. Значение по умолчанию — 1000. Этот параметр можно задать только при запуске сервера.

pgpro_multiplan.sandbox (boolean) #

Включает резервирование отдельных зон разделяемой памяти для ведущего и резервного узла, что позволяет тестировать и анализировать запросы с существующим набором данных без влияния на работу узла. Если на резервном узле установлено значение on, pgpro_multiplan замораживает планы выполнения запросов только на этом узле и хранит их в альтернативном хранилище планов — «песочнице». Если параметр включён на ведущем узле, расширение использует отдельную зону разделяемой памяти, данные которой не реплицируются на резервные узлы. При изменении значения параметра сбрасывается кеш pgpro_multiplan. Значение по умолчанию — off. Изменить этот параметр могут только суперпользователи.

pgpro_multiplan.wildcards (string) #

Регулярное выражение POSIX, которое используется для планов с типом template для проверки соответствия имён таблиц, задействованных в запросах. Значение pgpro_multiplan.wildcards замораживается вместе с соответствующим запросом. Значение по умолчанию — .*, которое означает соответствие любому имени таблицы.

pgpro_multiplan.aqe_max_items (integer) #

Задаёт максимальное количество хранимых индивидуальных значений триггеров AQE. Значение по умолчанию — 100. Этот параметр можно задать только при запуске сервера.

pgpro_multiplan.aqe_max_stats (integer) #

Задаёт максимальное количество собираемых статистических значений AQE. Дальнейшие статистические значения игнорируются. Значение по умолчанию — 5000. Этот параметр можно задать только при запуске сервера.

pgpro_multiplan.global_prepared_statements (boolean) #

Включает функциональность хранения и использования глобальных подготовленных операторов для нескольких сеансов в рамках текущего сервера. Значение по умолчанию — off. Изменить этот параметр могут только суперпользователи.

pgpro_multiplan.global_prepared_statements_allow_duplicate (boolean) #

Разрешает создание идентичных глобальных подготовленных операторов без возникновения ошибок. Значение по умолчанию — off. Изменить этот параметр могут только суперпользователи.