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

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

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

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

G.2.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.2.5.

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

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

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

    CREATE EXTENSION pgpro_multiplan;

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

  4. По умолчанию расширение pgpro_multiplan выключено. Включить его можно одним из следующих способов:

    • Чтобы включить pgpro_multiplan для всех серверов, в файле postgresql.conf необходимо указать pgpro_multiplan.enable = true.

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

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

G.2.3. Использование #

Расширение pgpro_multiplan можно использовать двумя способами: либо с замороженными, либо с разрешёнными планами.

G.2.3.1. Замороженные планы #

Расширение pgpro_multiplan позволяет замораживать планы запросов для дальнейшего использования. Заморозка состоит из трёх этапов:

  1. Регистрация запроса, план которого необходимо заморозить.

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

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

G.2.3.1.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
      ->  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))
G.2.3.1.2. Изменение плана выполнения запроса #

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

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

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

G.2.3.1.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.enable = 'on';

-- Зарегистрируйте запрос
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)

-- Измените план выполнения запроса
-- Запустите сканирование индекса, отключив последовательное сканирование
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;
SET enable_seqscan = 'off';

-- Заморозьте план выполнения запроса
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.2.3.2. Разрешённые планы #

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

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

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

Примечание

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

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

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

-- Создайте таблицу '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.auto_capturing = 'on';
SET pgpro_multiplan.enable = '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  | Custom Scan (MultiplanScan)  (cost=36.77..36.78 rows=1 width=8)                                     +
              |   Output: (count(*))                                                                                +
              |   Plan is: tracked                                                                                  +
              |   SQL hash: 6079808577596655075                                                                     +
              |   Const hash: 0                                                                                     +
              |   Plan hash: -487722818968417375                                                                    +
              |   Parameters: 0                                                                                     +
              |   ->  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  | Custom Scan (MultiplanScan)  (cost=18.99..19.00 rows=1 width=8)                                     +
              |   Output: (count(*))                                                                                +
              |   Plan is: tracked                                                                                  +
              |   SQL hash: 6079808577596655075                                                                     +
              |   Const hash: 0                                                                                     +
              |   Plan hash: 2719320099967191582                                                                    +
              |   Parameters: 0                                                                                     +
              |   ->  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.2.3.2.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
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         | <>
hintstr      | Leading(("t1" "t2" )) HashJoin("t1" "t2")  IndexScan("t2" "a_y_idx") SeqScan("t1")
-[ RECORD 2 ]+------------------------------------------------------------------------------------------------
dbid         | 5
sql_hash     | 6079808577596655075
const_hash   | 0
plan_hash    | 2719320099967191582
valid        | t
cost         | 18.997500000000002
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         | <>
hintstr      | Leading(("t2" "t1" )) HashJoin("t1" "t2")  IndexScan("t2" "a_y_idx") IndexScan("t1" "a_y_idx")
G.2.3.2.3. Пример разрешённого плана #

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

-- Включите параметр auto_capturing
SET pgpro_multiplan.auto_capturing = 'on';
SET pgpro_multiplan.enable = '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
--------------------------------------------------------------------------------
Custom Scan (MultiplanScan) (actual rows=1 loops=1)
  Plan is: tracked
  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.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
--------------------------------------------------------------------------------
Custom Scan (MultiplanScan) (actual rows=1 loops=1)
  Plan is: tracked
  SQL hash: 6079808577596655075
  Const hash: 0
  Plan hash: 2719320099967191582
  ->  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: frozen, hintset
  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: frozen, hintset
  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.2.4. Типы замороженных планов #

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

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

  • План hintset — это план с набором указаний, формируемым в момент заморозки на основе плана выполнения. Этот набор состоит из значений переменных окружения оптимизатора, отличных от используемых по умолчанию, типов соединений, порядка соединений и методов доступа к данным. Указания соответствуют указаниям, которые поддерживаются расширением pg_hint_plan. Для использования таких планов необходимо включить расширение pg_hint_plan. Набор указаний передаётся планировщику pg_hint_plan при первом нахождении соответствующего замороженного запроса, после чего pg_hint_plan формирует выполняемый план. В случае отсутствия данного расширения указания игнорируются и выполняется план, сформированный оптимизатором Postgres Pro. План с указаниями не зависит от идентификаторов объектов и остаётся действительным при пересоздании таблиц, добавлении полей и других изменениях. На данный момент разрешённые планы могут быть только типа hintset.

  • План template — это частный случай плана hintset. Если для сопоставления имён таблиц в запросах и замороженном запросе используются одни и те же регулярные выражения POSIX, используется план template.

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

Для обеспечения совместимости 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.2.6. Идентификация замороженного запроса #

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

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

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

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

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.2.8. Представления #

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

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

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

ИмяТипОписание
dbidoidИдентификатор базы данных, в которой выполнялся оператор
sql_hashbigintВнутренний идентификатор запроса
const_hashbigintХеш непараметризованных констант
validbooleanFALSE, если план был аннулирован при последнем использовании
query_stringtextЗапрос, зарегистрированный функцией pgpro_multiplan_register_query
paramtypesregtype[]Массив с типами параметров, использованными в запросе
querytextВнутреннее представление запроса
plantextВнутреннее представление плана
plan_typetextТип плана: serialized, hintset или template
hintstrtextНабор указаний, сформированный на основе замороженного плана
wildcardstextШаблоны, используемые для плана template, NULL для остальных типов планов

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

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

Таблица G.3. Столбцы 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.2.8.3. Представление pgpro_multiplan_captured_queries #

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

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

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

G.2.8.4. Представление pgpro_multiplan_fs_counter #

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

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

ИмяТипОписание
dbidoidИдентификатор базы данных, в которой выполнялся оператор
sql_hashbigintВнутренний идентификатор запроса
plan_hashbigintВнутренний идентификатор плана
usage_numbtextСчётчик использования замороженных операторов

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

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

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

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

pgpro_multiplan_unregister_query() returns bool #

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

pgpro_multiplan_freeze(plan_type text) returns bool #

Замораживает последний использованный план для оператора. Допустимые значения необязательного аргумента plan_type: serialized, hintset и template. Значение serialized показывает, что используется план запроса, основанный на сериализованном представлении. При использовании hintset pgpro_multiplan использует план запроса на основе набора указаний, который формируется на этапе выполнения зарегистрированного запроса. При использовании template pgpro_multiplan создаёт план с шаблонами, который может применяться к запросам с именами таблиц, которые совпадают с регулярными выражениями, указанными в параметре конфигурации pgpro_multiplan.wildcards. Содержимое pgpro_multiplan.wildcards замораживается вместе с запросом для планов template. Если аргумент plan_type опущен, по умолчанию используется 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 для указанной базы данных. Чтобы удалить данные, собранные pgpro_multiplan для текущей базы данных, не указывайте dbid. Чтобы сбросить данные для всех баз данных, установите для параметра dbid значение NULL.

pgpro_multiplan_reload_frozen_plancache() returns bool #

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

pgpro_multiplan_fs_counter() returns table #

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

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. Возвращает true, если запрос был успешно перемещён.

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

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

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

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

pgpro_multiplan_captured_clean() returns bigint #

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

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

pgpro_multiplan.enable (boolean) #

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

pgpro_multiplan.fs_ctr_max (integer) #

Задаёт максимальное количество замороженных операторов, возвращаемых функцией pgpro_multiplan_fs_counter(). Значение по умолчанию — 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. Значение по умолчанию — .*, которое соответствует любому имени таблицы. Регулярные выражения применяются слева направо. Например, в ^t[[:digit:]]$,^t.*,.* первое проверенное регулярное выражение — ^t[[:digit:]]$, следующее — ^t.*, и последнее — .*.