Перейти к основному содержимому

auto_dump. Сбор и воспроизведение проблемных запросов

В исходном дистрибутиве установлено по умолчанию: нет.

Связанные компоненты: отсутствуют.

Схема размещения: не используется.

Данное расширение реализует процесс получения воспроизведения медленного выполнения запроса без использования платформы 1С:Предприятие. Расширение:

  • Формирует выгрузку таблиц, которые используются в запросе. Можно включать в выгрузку, как временные таблицы, так и постоянные таблицы;
  • Создает SQL-запрос для переноса и подготовки данных для воспроизведения проблемного запроса;
  • Создает файл проблемного SQL-запроса для его воспроизведения и получает его план выполнения (с помощью команды EXPLAIN).

Доработка

Доработка не проводилась.

Установка

Для начала использования расширения выполните следующие действия:

  1. Пропишите в конфигурационном файле (postgresql.conf/postgres.yml) СУБД Pangolin название расширения в настроечный параметр shared_preload_libraries:

    shared_preload_libraries = 'auto_dump'
  2. Включите расширение путем заполнения основного конфигурационного параметра:

    auto_dump.enable = on
  3. Настройте расширение, для этого выполните конфигурирование дополнительных параметров.

  4. Выполните перезапуск сервера:

    systemctl restart postgresql
  5. Далее расширение нужно установить в целевую БД командой:

    CREATE EXTENSION auto_dump;

Настройка

Конфигурационные параметры

Основные параметры

Параметр

Тип данных

Значение по умолчанию

Описание

auto_dump.enable

boolean

off

Признак включения расширения

auto_dump.output_directory

string

Путь директории, в которую будут сохраняться файлы с дампами таблиц. Параметр обязателен для настройки. У пользователя, под которым запущена служба СУБД, должны быть права на доступ к этой директории

Параметры, определяющие объем данных в дампе

Параметр

Тип данных

Значение по умолчанию

Описание

auto_dump.dump_temporary_tables

boolean

on

При включении параметра в дамп будут добавлены временные таблицы сессии, участвующие в текущем запросе

auto_dump.dump_persistent_tables

boolean

off

При включении параметра в дамп будут добавлены все постоянные таблицы, участвующие в текущем запросе

auto_dump.dump_all_temp_tables

boolean

off

При включении параметра в дамп будут добавлены все временные таблицы сессии, в том числе и участвующие в текущем запросе

auto_dump.dump_data

boolean

on

При включении параметра в дамп будет записано содержимое таблиц, для которых снимается дамп. Если параметр выключен, то в SQL-запросе будет описано только создание таблиц, без их заполнения

auto_dump.dump_indexes

boolean

on

При включении параметра в дамп будет записано создание индексов для таблиц, которые участвуют в дампе

auto_dump.dump_copy_data

boolean

off

Определяет метод выгрузки данных таблиц

Если параметр включен, данные таблиц выгружаются с помощью команды COPY TO. В результате для каждой таблицы создается отдельный файл .txt, на который дается ссылка в операторе COPY ... FROM в файле выгрузки. Если параметр отключен, данные таблиц выгружаются в файл выгрузки в виде операторов INSERT, содержащих все значения таблиц.

auto_dump.dump_query

boolean

on

При включении параметра будет сформирован файл с SQL-запросом, для которого сформирован дамп

auto_dump.dump_create

boolean

on

При включении параметра будет сформирован файл с SQL-командами создания таблиц, для которых снимается дамп

auto_dump.dump_plan

boolean

on

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

Параметры, включающие критерии срабатывания

Параметр

Тип данных

Значение по умолчанию

Описание

auto_dump.dump_on_query_string

string

Фрагмент SQL-запроса, при появлении которого в запросе будет осуществлено срабатывание получения дампа

auto_dump.dump_on_cancel

boolean

off

При включении параметра будет осуществляться дамп запросов отмененных со стороны СУБД (например, командой pg_cancel_backend или в момент lock timeout)

auto_dump.dump_on_bad_plan

boolean

off

При включении параметра будет анализироваться ожидаемое и фактическое количество строк для всех запросов. Чтобы сработало автоматическое формирование дампа по запросу, должны одновременно выполниться два критерия auto_dump.bad_plan_percent_threshold и auto_dump.bad_plan_count_threshold, которые определяют запрос как «проблемный»

auto_dump.bad_plan_count_threshold

integer

0

Критерий срабатывания автоматического дампа по абсолютной разнице между ожидаемым и фактическим количеством строк.

Анализируются все узлы плана. Если ожидаемое количество строк отличается от фактического количества строк более чем на заданное в параметре число, то критерий считается выполнен. Если значение равно 0, то критерий считается выполненным всегда.

auto_dump.bad_plan_percent_threshold

integer

0

Критерий срабатывания автоматического дампа по разнице в процентах между ожидаемым и фактическим количеством строк

В качестве параметра устанавливается число от 0 до 100. Анализируются все узлы плана. По каждому узлу проверяется отношение суммы ожидаемого количества строк и фактического количества строк к ожидаемому количеству строк. Если это отношение превышает установленный параметр, то критерий считается выполненным. Если значение равно 0, то критерий считается выполненным всегда

auto_dump.dump_on_time

boolean

off

Сохранить выгрузку запроса, если время его выполнения превышает значение auto_dump.timeout

auto_dump.timeout

integer

0

Задает значение тайм-аута для auto_dump.dump_on_time в миллисекундах. Возможные значения — положительные целые числа. Если для параметра задано значение 0, выгружаются все запросы

Ограничения

Не рекомендуется настраивать auto_dump таким образом, чтобы оно выгружало каждый запрос. Расширение предназначено для отладки и не может использоваться для непрерывного мониторинга.

Ограничения:

  • Расширение auto_dump несовместимо с командой PREPARE TRANSACTION при работе с временными таблицами.
  • Если включен параметр auto_dump.dump_all_temp_tables, расширение auto_dump не может обратиться к временным пространствам имен из автономных транзакций.

Использование модуля

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

  1. С помощью технологического журнала был получен текст SQL-запроса:

    SELECT
    T3._Q_001_F_000RRef,
    T3._Q_001_F_001,
    T1._Q_001_F_002,
    T1._Q_001_F_001RRef,
    T2._Fld23
    FROM pg_temp.tt3 T1
    LEFT OUTER JOIN _Reference7X1 T2
    ON (T1._Q_001_F_001RRef = T2._IDRRef)
    INNER JOIN pg_temp.tt2 T3
    ON (T1._Q_001_F_000RRef = T3._Q_001_F_000RRef)
  2. В конфигурационном файле postgresql.conf/postgres.yml были добавлены строки:

    shared_preload_libraries = ‘auto_dump’
    auto_dump.enable = on
    auto_dump.output_directory = ‘/tmp/auto_d’
    auto_dump.dump_on_query_string = ‘LEFT OUTER JOIN _Reference7X1’
  3. Перезапуск службы СУБД:

    user@server: sudo systemctl restart postgresql@16-main.service
  4. Добавление расширения в базу данных auto_d:

    user@server: psql -h localhost -U postgres -d auto_d
    auto_d=# create extension auto_dump;

    CREATE EXTENSION
  5. Пользователь в 1С выполнил действие, которое использует проблемный запрос. После этого в каталоге /tmp/auto_d появился новый каталог, который имеет следующий формат PIDПроцесса-ГГГГ-ММ-ДД-ЧЧ-ММ-СС-ПорядковыйНомер.

    user@server:/tmp/auto_d/71491-2025_06_11_12_42_36_00$ ls -l
    -rw------- 1 postgres postgres 3241 июн 11 12:42 create.sql
    -rw------- 1 postgres postgres 6743 июн 11 12:42 createwithdata.sql

    Файл create.sql содержит текст:

    CREATE TEMPORARY TABLE tt3 (
    _q_001_f_000rref bytea,
    _q_001_f_001rref bytea,
    _q_001_f_002 numeric(22,2)
    );

    CREATE INDEX tmpind_1 ON pg_temp.tt3 USING btree (_q_001_f_000rref);

    ANALYZE tt3;

    CREATE TEMPORARY TABLE tt2 (
    _q_001_f_000rref bytea,
    _q_001_f_001 numeric(16,2)
    );

    CREATE INDEX tmpind_0 ON pg_temp.tt2 USING btree (_q_001_f_000rref);

    ANALYZE tt2;

    /*
    SELECT
    T3._Q_001_F_000RRef,
    T3._Q_001_F_001,
    T1._Q_001_F_002,
    T1._Q_001_F_001RRef,
    T2._Fld23
    FROM pg_temp.tt3 T1
    LEFT OUTER JOIN _Reference7X1 T2
    ON (T1._Q_001_F_001RRef = T2._IDRRef)
    INNER JOIN pg_temp.tt2 T3
    ON (T1._Q_001_F_000RRef = T3._Q_001_F_000RRef);

    */
    /*
    Query Text: SELECT
    T3._Q_001_F_000RRef,
    T3._Q_001_F_001,
    T1._Q_001_F_002,
    T1._Q_001_F_001RRef,
    T2._Fld23
    FROM pg_temp.tt3 T1
    LEFT OUTER JOIN _Reference7X1 T2
    ON (T1._Q_001_F_001RRef = T2._IDRRef)
    INNER JOIN pg_temp.tt2 T3
    ON (T1._Q_001_F_000RRef = T3._Q_001_F_000RRef)
    Hash Join (cost=8.01..9.51 rows=22 width=79)
    Hash Cond: (t1._q_001_f_000rref = t3._q_001_f_000rref)
    -> Hash Left Join (cost=6.76..8.01 rows=22 width=72)
    Hash Cond: (t1._q_001_f_001rref = t2._idrref)
    -> Seq Scan on tt3 t1 (cost=0.00..1.22 rows=22 width=40)
    -> Hash (cost=6.36..6.36 rows=36 width=64)
    -> Seq Scan on _reference7x1 t2 (cost=0.00..6.36 rows=36 width=64)
    -> Hash (cost=1.12..1.12 rows=12 width=24)
    -> Seq Scan on tt2 t3 (cost=0.00..1.12 rows=12 width=24)
    */
    /*
    Query Text: SELECT
    T3._Q_001_F_000RRef,
    T3._Q_001_F_001,
    T1._Q_001_F_002,
    T1._Q_001_F_001RRef,
    T2._Fld23
    FROM pg_temp.tt3 T1
    LEFT OUTER JOIN _Reference7X1 T2
    ON (T1._Q_001_F_001RRef = T2._IDRRef)
    INNER JOIN pg_temp.tt2 T3
    ON (T1._Q_001_F_000RRef = T3._Q_001_F_000RRef)
    Hash Join (cost=8.01..9.51 rows=22 width=79) (actual time=0.027..0.037 rows=22 loops=1)
    Output: t3._q_001_f_000rref, t3._q_001_f_001, t1._q_001_f_002, t1._q_001_f_001rref, t2._fld23
    Hash Cond: (t1._q_001_f_000rref = t3._q_001_f_000rref)
    Buffers: shared hit=6, local hit=2
    -> Hash Left Join (cost=6.76..8.01 rows=22 width=72) (actual time=0.020..0.025 rows=22 loops=1)
    Output: t1._q_001_f_002, t1._q_001_f_001rref, t1._q_001_f_000rref, t2._fld23
    Inner Unique: true
    Hash Cond: (t1._q_001_f_001rref = t2._idrref)
    Buffers: shared hit=6, local hit=1
    -> Seq Scan on pg_temp.tt3 t1 (cost=0.00..1.22 rows=22 width=40) (actual time=0.001..0.002 rows=22 loops=1)
    Output: t1._q_001_f_000rref, t1._q_001_f_001rref, t1._q_001_f_002
    Buffers: local hit=1
    -> Hash (cost=6.36..6.36 rows=36 width=64) (actual time=0.016..0.016 rows=36 loops=1)
    Output: t2._fld23, t2._idrref
    Buckets: 1024 Batches: 1 Memory Usage: 11kB
    Buffers: shared hit=6
    -> Seq Scan on public._reference7x1 t2 (cost=0.00..6.36 rows=36 width=64) (actual time=0.003..0.011 rows=36 loops=1)
    Output: t2._fld23, t2._idrref
    Buffers: shared hit=6
    -> Hash (cost=1.12..1.12 rows=12 width=24) (actual time=0.005..0.005 rows=12 loops=1)
    Output: t3._q_001_f_000rref, t3._q_001_f_001
    Buckets: 1024 Batches: 1 Memory Usage: 9kB
    Buffers: local hit=1
    -> Seq Scan on pg_temp.tt2 t3 (cost=0.00..1.12 rows=12 width=24) (actual time=0.002..0.002 rows=12 loops=1)
    Output: t3._q_001_f_000rref, t3._q_001_f_001
    Buffers: local hit=1
    Settings: enable_mergejoin = 'off', cpu_operator_cost = '0.001'
    */

    Файл createwithdata.sql содержит текст:

    CREATE TEMPORARY TABLE tt3 (
    _q_001_f_000rref bytea,
    _q_001_f_001rref bytea,
    _q_001_f_002 numeric(22,2)
    );

    CREATE INDEX tmpind_1 ON pg_temp.tt3 USING btree (_q_001_f_000rref);

    INSERT INTO tt3 (_q_001_f_000rref, _q_001_f_001rref, _q_001_f_002) VALUES
    (E'\\x8ca0000d8843cd1b11dc8d043d710079',E'\\x8ca1000d8843cd1b11dc8ea92d97a749',36960.00),

    (E'\\x8ca1000d8843cd1b11dc8ea92d97a74a',E'\\x8ca1000d8843cd1b11dc8ea92d97a749',123750.00);

    ANALYZE tt3;

    CREATE TEMPORARY TABLE tt2 (
    _q_001_f_000rref bytea,
    _q_001_f_001 numeric(16,2)
    );

    CREATE INDEX tmpind_0 ON pg_temp.tt2 USING btree (_q_001_f_000rref);

    INSERT INTO tt2 (_q_001_f_000rref, _q_001_f_001) VALUES
    (E'\\x8ca0000d8843cd1b11dc8d043d71007d',59500.00),

    (E'\\x8ca0000d8843cd1b11dc8d043d710076',153176.73);

    ANALYZE tt2;

    /*
    SELECT
    T3._Q_001_F_000RRef,
    T3._Q_001_F_001,
    T1._Q_001_F_002,
    T1._Q_001_F_001RRef,
    T2._Fld23
    FROM pg_temp.tt3 T1
    LEFT OUTER JOIN _Reference7X1 T2
    ON (T1._Q_001_F_001RRef = T2._IDRRef)
    INNER JOIN pg_temp.tt2 T3
    ON (T1._Q_001_F_000RRef = T3._Q_001_F_000RRef);

    */
    /*
    Query Text: SELECT
    T3._Q_001_F_000RRef,
    T3._Q_001_F_001,
    T1._Q_001_F_002,
    T1._Q_001_F_001RRef,
    T2._Fld23
    FROM pg_temp.tt3 T1
    LEFT OUTER JOIN _Reference7X1 T2
    ON (T1._Q_001_F_001RRef = T2._IDRRef)
    INNER JOIN pg_temp.tt2 T3
    ON (T1._Q_001_F_000RRef = T3._Q_001_F_000RRef)
    Hash Join (cost=8.01..9.51 rows=22 width=79)
    Hash Cond: (t1._q_001_f_000rref = t3._q_001_f_000rref)
    -> Hash Left Join (cost=6.76..8.01 rows=22 width=72)
    Hash Cond: (t1._q_001_f_001rref = t2._idrref)
    -> Seq Scan on tt3 t1 (cost=0.00..1.22 rows=22 width=40)
    -> Hash (cost=6.36..6.36 rows=36 width=64)
    -> Seq Scan on _reference7x1 t2 (cost=0.00..6.36 rows=36 width=64)
    -> Hash (cost=1.12..1.12 rows=12 width=24)
    -> Seq Scan on tt2 t3 (cost=0.00..1.12 rows=12 width=24)
    */
    /*
    Query Text: SELECT
    T3._Q_001_F_000RRef,
    T3._Q_001_F_001,
    T1._Q_001_F_002,
    T1._Q_001_F_001RRef,
    T2._Fld23
    FROM pg_temp.tt3 T1
    LEFT OUTER JOIN _Reference7X1 T2
    ON (T1._Q_001_F_001RRef = T2._IDRRef)
    INNER JOIN pg_temp.tt2 T3
    ON (T1._Q_001_F_000RRef = T3._Q_001_F_000RRef)
    Hash Join (cost=8.01..9.51 rows=22 width=79) (actual time=0.027..0.037 rows=22 loops=1)
    Output: t3._q_001_f_000rref, t3._q_001_f_001, t1._q_001_f_002, t1._q_001_f_001rref, t2._fld23
    Hash Cond: (t1._q_001_f_000rref = t3._q_001_f_000rref)
    Buffers: shared hit=6, local hit=2
    -> Hash Left Join (cost=6.76..8.01 rows=22 width=72) (actual time=0.020..0.025 rows=22 loops=1)
    Output: t1._q_001_f_002, t1._q_001_f_001rref, t1._q_001_f_000rref, t2._fld23
    Inner Unique: true
    Hash Cond: (t1._q_001_f_001rref = t2._idrref)
    Buffers: shared hit=6, local hit=1
    -> Seq Scan on pg_temp.tt3 t1 (cost=0.00..1.22 rows=22 width=40) (actual time=0.001..0.002 rows=22 loops=1)
    Output: t1._q_001_f_000rref, t1._q_001_f_001rref, t1._q_001_f_002
    Buffers: local hit=1
    -> Hash (cost=6.36..6.36 rows=36 width=64) (actual time=0.016..0.016 rows=36 loops=1)
    Output: t2._fld23, t2._idrref
    Buckets: 1024 Batches: 1 Memory Usage: 11kB
    Buffers: shared hit=6
    -> Seq Scan on public._reference7x1 t2 (cost=0.00..6.36 rows=36 width=64) (actual time=0.003..0.011 rows=36 loops=1)
    Output: t2._fld23, t2._idrref
    Buffers: shared hit=6
    -> Hash (cost=1.12..1.12 rows=12 width=24) (actual time=0.005..0.005 rows=12 loops=1)
    Output: t3._q_001_f_000rref, t3._q_001_f_001
    Buckets: 1024 Batches: 1 Memory Usage: 9kB
    Buffers: local hit=1
    -> Seq Scan on pg_temp.tt2 t3 (cost=0.00..1.12 rows=12 width=24) (actual time=0.002..0.002 rows=12 loops=1)
    Output: t3._q_001_f_000rref, t3._q_001_f_001
    Buffers: local hit=1
    Settings: enable_mergejoin = 'off', cpu_operator_cost = '0.001'
    */

Ссылки на документацию разработчика

Дополнительно поставляемый модуль auto_dump.