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

pg_dbms_stats. Стабилизация планировщика запросов

Версия: 15.0.

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

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

Схема размещения: dbms_stats.

Расширение pg_dbms_stats позволяет управлять поведением планировщика за счет хранения таблиц статистики.

СУБД Pangolin управляет статистикой таблиц на основе выборочных значений таблиц и индексов с помощью команды ANALYZE. Оптимизатор запросов рассчитывает стоимость плана выполнения на основе этой статистики и выбирает план с наименьшей стоимостью. В таком случае неточные статистические данные или резкие изменения объема либо распределения данных могут привести к тому, что оптимизатор запросов выберет неожиданный или нежелательный план выполнения.

Расширение pg_dbms_stats предотвращает перепланирование запроса через механизм merge двух составляющих: реальной статистики pg_statistic и собственной статистики, ранее сохраненной во внутренних структурах расширения. Принцип работы расширения pg_dbms_stats заключается в подмене настоящей статистики на ранее сохраненную (заблокированную) в структурах хранения расширения.

pg_dbms_stats может фиксировать статистику для следующих объектов:

  • таблицы;
  • индексы (с ограничениями, кроме функциональных индексов);
  • базы данных;
  • столбцы.

Основная функциональность расширения представлена в таблице:

Команда

Описание

Backup

Создает резервную копию текущей статистики

Restore

Восстанавливает статистику из резервной копии и фиксирует ее

Purge

Удаляет более не нужные резервные копии

Lock

Фиксирует текущую статистику

Unlock

Снимает фиксацию статистики

Cleanup

Освобождает фиксацию статистики (массово удаляет все неиспользуемые записи)

Export

Выгружает статистику во внешний файл (бинарный формат)

Import

Загружает статистику из внешнего файла и фиксирует ее

Доработка

Не производилась.

Ограничения

Экспорт статистики через pg_dbms_stats доступен только для редакций Enterprise и Enterprise для ERP-систем.

Установка

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

  1. Создайте схему dbms_stats:

    CREATE SCHEMA dbms_stats;
  2. Установите расширение в целевую БД:

    CREATE EXTENSION pg_dbms_stats SCHEMA dbms_stats;
  3. Включите расширение через настройку параметра:

    SET pg_dbms_stats.use_locked_stats TO on;
    примечание

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

Настройка

Таблицы

Следующая таблица содержит описание основных таблиц расширения pg_dbms_stats:

Имя таблицы

Описание

dbms_stats.relation_stats_locked

Содержит статистику на уровне таблиц для использования планировщиком запросов вместо стандартной статистики из pg_class

dbms_stats.column_stats_locked

Содержит статистику на уровне столбцов для использования планировщиком запросов вместо стандартной статистики из pg_stats

dbms_stats.backup_history

Хранит историю резервных копий статистики, включая идентификатор резервной копии и связанные с ней объекты

dbms_stats.relation_stats_backup

Содержит резервные копии статистики на уровне таблиц

dbms_stats.column_stats_backup

Содержит резервные копии статистики на уровне столбцов

Функции

Триггерные функции

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

Имя функции

Описание

dbms_stats.invalidate_relation_cache

Триггерная функция для сброса кеша статистики таблицы

dbms_stats.invalidate_column_cache

Триггерная функция для сброса кеша статистики столбца

Резервирование статистики

Функции для создания резервных копий статистики различных объектов базы данных:

Имя функции

Описание

dbms_stats.backup

Основная функция резервного копирования статистики

dbms_stats.backup_database_stats

Создает резервную копию статистики всей базы данных

dbms_stats.backup_schema_stats

Создает резервную копию статистики для схемы

dbms_stats.backup_table_stats

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

dbms_stats.backup_column_stats

Создает резервную копию статистики для столбца

Восстановление статистики

Функции для восстановления статистики из ранее созданных резервных копий и при необходимости фиксирование ее для планировщика:

Имя функции

Описание

dbms_stats.restore_database_stats

Восстанавливает статистику всей базы данных из резервной копии и фиксирует ее

dbms_stats.restore_schema_stats

Восстанавливает статистику для схемы

dbms_stats.restore_column_stats

Восстанавливает статистику для столбца

dbms_stats.restore_stats

Восстанавливает выбранные резервные копии статистики

Блокировка статистики

Функции для фиксации статистики, чтобы текущий план выполнения запросов оставался неизменным:

Имя функции

Описание

dbms_stats.lock_database_stats

Блокирует статистику всей базы данных

dbms_stats.lock_schema_stats

Блокирует статистику схемы

dbms_stats.lock_table_stats

Блокирует статистику таблицы

dbms_stats.lock_column_stats

Блокирует статистику столбца

Снятие блокировки статистики

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

Имя функции

Описание

dbms_stats.unlock_database_stats

Снимает блокировку статистики всей базы данных

dbms_stats.unlock_schema_stats

Снимает блокировку статистики схемы

dbms_stats.unlock_table_stats

Снимает блокировку статистики таблицы

dbms_stats.unlock_column_stats

Снимает блокировку статистики столбца

Импорт статистики

Функции для загрузки статистики из внешних файлов и ее фиксирование для использования планировщиком:

Имя функции

Описание

dbms_stats.import_database_stats

Импортирует статистику всей базы данных и фиксирует ее

dbms_stats.import_schema_stats

Импортирует статистику схемы и фиксирует ее

dbms_stats.import_table_stats

Импортирует статистику таблицы и фиксирует ее

dbms_stats.import_column_stats

Импортирует статистику столбца и фиксирует ее

Экспорт статистики

Функции для экспорта статистики во внешний файл для последующего импорта или анализа:

Сведения

Функциональность доступна только для редакций Enterprise и Enterprise для ERP-систем.

Имя функции

Описание

dbms_stats.export_database_stats

Экспортирует статистику всей базы данных во внешний файл

dbms_stats.export_schema_stats

Экспортирует статистику схемы во внешний файл

dbms_stats.export_table_stats

Экспортирует статистику таблицы во внешний файл

dbms_stats.export_column_stats

Экспортирует статистику столбца во внешний файл

Очистка статистики

Функции для удаления устаревших или неиспользуемых резервных копий и заблокированных статистик:

Функция

Описание

dbms_stats.purge_stats

Удаляет ненужные резервные копии статистики

dbms_stats.clean_up_stats

Удаляет все неиспользуемые заблокированные статистики

Копирование статистики

Функция dbms_stats.copy_table_stats копирует статистику между таблицами или внутри таблицы.

Управление

Блокировка статистики

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

Чтобы отключить использование расширения для конкретного объекта (таблицы или индекса), достаточно удалить данные по этому объекту из представлений dbms_stats.relation_stats_locked и dbms_stats.column_stats_locked.

Просмотр текущих зафиксированных статистик таблиц, используемых планировщиком:

SELECT * FROM dbms_stats.relation_stats_locked;

relid | relname | relpages | reltuples | relallvisible | curpages | last_analyze | last_autoanalyze | purge_existing_plan
-------+---------+----------+-----------+---------------+----------+--------------+------------------+---------------------
(0 rows)

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

SELECT * FROM dbms_stats.relation_stats_backup;

id | relid | relname | relpages | reltuples | relallvisible | curpages | last_analyze | last_autoanalyze
----+-------+---------+----------+-----------+---------------+----------+--------------+------------------
(0 rows)

Блокировка статистики всех объектов в текущей базе данных:

SELECT dbms_stats.lock_database_stats();

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

    lock_database_stats
---------------------------
table_part2
idx2
pt0
pt0_idx
st0
st0_idx
st1
s0.sft0
s0.sft0_idx
s0.st0
s0.st0_idx
s0.st1
s0.st1_idx
s0.st2
s0.st2_idx
s1.st0
st1_idx
st1_exp
st3
s0000.table_part_volume_1
s0000.table_part_volume_2
s0000.table_part_volume_3
s0000.table_part_volume_4
s0.test1
s0.tes
s0.test
table_part_volume_1
table_part_volume_2
table_part_volume_3
table_part_volume_4
(30 rows)

После выполнения команды dbms_stats.lock_database_stats(), которая блокирует сбор и обновление статистики для всех объектов текущей базы данных, можно убедиться в успешности операции, запросив список заблокированных объектов. Результаты ниже подтверждают, что статистика для указанных таблиц и индексов теперь зафиксирована и будет использоваться планировщиком без изменений:

SELECT * FROM dbms_stats.relation_stats_locked;

Результат:

 relid  |          relname          | relpages | reltuples | relallvisible | curpages |         last_analyze          |  last_autoanalyze | purge_existing_plan
--------+---------------------------+----------+-----------+---------------+----------+-------------------------------+-------------------+---------------------
16784 | ext.table_part2 | 0 | 0 | 0 | 0 | | | f
16791 | ext.idx2 | 1 | 0 | 0 | 1 | | | f
27204 | ext.pt0 | 0 | 0 | 0 | 0 | | | f
27207 | ext.pt0_idx | 1 | 0 | 0 | 1 | | | f
27208 | ext.st0 | 1 | 2 | 1 | 1 | | | f
27211 | ext.st0_idx | 2 | 2 | 0 | 2 | | | f
27212 | ext.st1 | 89 | 10000 | 89 | 89 | | | f
27218 | s0.sft0 | 0 | 0 | 0 | 0 | | | f
27221 | s0.sft0_idx | 1 | 0 | 0 | 1 | | | f
27222 | s0.st0 | 1 | 2 | 1 | 1 | | | f
27225 | s0.st0_idx | 2 | 2 | 0 | 2 | | | f
27226 | s0.st1 | 1 | 3 | 1 | 1 | | | f
27229 | s0.st1_idx | 2 | 3 | 0 | 2 | | | f
27230 | s0.st2 | 1 | 1 | 0 | 1 | | | f
27235 | s0.st2_idx | 2 | 3 | 0 | 2 | | | f
27245 | s1.st0 | 1 | 4 | 0 | 1 | | | f
27274 | ext.st1_idx | 11 | 10000 | 0 | 11 | | | f
27275 | ext.st1_exp | 11 | 10000 | 0 | 11 | | | f
27286 | ext.st3 | 0 | 0 | 0 | 0 | | | f
28432 | s0000.table_part_volume_1 | 0 | 0 | 0 | 0 | | | f
28438 | s0000.table_part_volume_2 | 0 | 0 | 0 | 0 | | | f
28444 | s0000.table_part_volume_3 | 0 | 0 | 0 | 0 | | | f
28450 | s0000.table_part_volume_4 | 0 | 0 | 0 | 0 | | | f
28785 | s0.test1 | 1 | 3 | 1 | 1 | | | f
28853 | s0.tes | 1 | 3 | 0 | 1 | | | f
29035 | s0.test | 1 | 3 | 0 | 1 | | | f
37046 | ext.table_part_volume_1 | 5406 | 999999 | 0 | 21622 | 2025-08-22 19:42:37.680004+03 | | f
37052 | ext.table_part_volume_2 | 5401 | 999001 | 0 | 21601 | 2025-08-22 19:42:37.871673+03 | | f
37058 | ext.table_part_volume_3 | 0 | 0 | 0 | 0 | 2025-08-22 19:42:37.873224+03 | | f
37064 | ext.table_part_volume_4 | 0 | 0 | 0 | 0 | 2025-08-22 19:42:37.873398+03 | | f
(30 rows)

С момента попадания объекта в dbms_stats.relation_stats_locked, дальнейшее выполнения планирования оптимизатором будет выполняться только с использованием сохраненной статистики.

Блокировка статистики с более точной детализацией (на уровне схем, конкретных таблиц или отдельных столбцов), например:

  • Блокировка статистики схемы ext:

    SELECT dbms_stats.lock_schema_stats('ext');

    Результат:

          lock_schema_stats
    ------------------------------
      table_part2
      idx2
      pt0
      pt0_idx
    st0
    st0_idx
    st1
      st1_idx
      st1_exp
      st3
      table_part_volume_1
      table_part_volume_2
      table_part_volume_3
      table_part_volume_4
      (14 rows)
  • Блокировка статистики таблицы table_part_volume_1 в схеме ext:

    SELECT dbms_stats.lock_table_stats('ext','table_part_volume_1');

    Результат:

      lock_table_stats
    ----------------------
    table_part_volume_1
    (1 row)
  • Блокировка статистики столбца id таблицы table_part_volume_1 в схеме ext:

    SELECT dbms_stats.lock_column_stats('ext','table_part_volume_1','id');

    Результат:

      lock_column_stats
    ----------------------
    table_part_volume_1
    (1 row)

Просмотр информации о заблокированной статистике в представлении dbms_stats.relation_stats_locked:

SELECT * FROM dbms_stats.relation_stats_locked;

Результат:

 relid |         relname          | relpages | reltuples | relallvisible | curpages | last_analyze | last_autoanalyze | purge_existing_plan
-------+--------------------------+----------+-----------+---------------+----------+--------------+------------------+---------------------
37075 | ext.table_part_volume_1 | | | | | | | f
(1 row)

Представление dbms_stats.column_stats_locked покажет более детальное содержание сохраненной статистики.

Снятие блокировки статистики

Если использование статистики для конкретного объекта базы данных утратило смысл из-за ее сильного устаревания либо несоразмерности, необходимо выполнить разблокировку статистики с помощью расширения:

SELECT dbms_stats.unlock_table_stats('ext','table_part_volume_1');

Результат:

   unlock_table_stats
------------------------
table_part_volume_1
(1 row)

Для разблокирования статистики можно пользоваться всем набором функций:

SELECT dbms_stats.unlock_database_stats();

Результат:

   unlock_database_stats
---------------------------
table_part_volume_1
(1 row)

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

Резервирование статистики

Резервирование статистики в произвольном интервале времени является одним из основных преимуществ расширения pg_dbms_stats. Объем сохраненной статистики не ограничен ни по объему ни по интервалу хранения.

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

  • dbms_stats.backup_database_stats() – на уровне БД;
  • dbms_stats.backup_schema_stats() – на уровне схемы;
  • dbms_stats.backup_table_stats() – на уровне таблицы;
  • dbms_stats.backup_column_stats() – на уровне столбца.

Резервирование статистики по 4 секциям одной таблицы:

SELECT dbms_stats.backup_table_stats('ext','table_part_volume_1','1 backup');

backup_table_stats
--------------------
12
(1 row)

SELECT dbms_stats.backup_table_stats('ext','table_part_volume_2','1 backup');

backup_table_stats
--------------------
13
(1 row)

SELECT dbms_stats.backup_table_stats('ext','table_part_volume_3','1 backup');

backup_table_stats
--------------------
14
(1 row)

SELECT dbms_stats.backup_table_stats('ext','table_part_volume_4','1 backup');

backup_table_stats
--------------------
15
(1 row)

В результате будут созданы 4 резервных копии статистики по 4 разным объектам:

SELECT * FROM dbms_stats.relation_stats_backup;

Результат:

 id | relid |         relname         | relpages | reltuples | relallvisible | curpages |         last_analyze          | last_autoanalyze
----+-------+-------------------------+----------+-----------+---------------+----------+-------------------------------+------------------
12 | 37075 | ext.table_part_volume_1 | 5406 | 999999 | 0 | 5406 | 2025-08-24 23:56:12.143525+03 |
13 | 37081 | ext.table_part_volume_2 | 5401 | 999001 | 0 | 5401 | 2025-08-24 23:56:12.143525+03 |
14 | 37087 | ext.table_part_volume_3 | 0 | 0 | 0 | 0 | 2025-08-24 23:56:12.143525+03 |
15 | 37093 | ext.table_part_volume_4 | 0 | 0 | 0 | 0 | 2025-08-24 23:56:12.143525+03 |
(4 rows)

По сохраненной статистике видно, что заполнены данными только первые 2 партиции таблицы. Статистика по всем столбцам (attribute) сохраняется в dbms_stats.column_stats_backup, что представляет собой снимок содержимого pg_statistic.

Каждая резервная копия статистики, созданная таким образом, имеет собственный идентификатор — поле id, которое обеспечивает связь между служебными каталогами хранения и используется, например, при копировании статистики между объектами.

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

SELECT dbms_stats.backup_database_stats ('Db Backup');

Результат:

 backup_database_stats
-----------------------
18
(1 row)

История резервирования статистики планировщика отражается в каталоге dbms_stats.backup_history:

SELECT * FROM dbms_stats.backup_history;

Результат:

 id |             time              | unit |  comment
----+-------------------------------+------+-----------
18 | 2025-08-25 22:56:52.478304+03 | d | Db Backup

Восстановление статистики

Процесс восстановления резервной копии статистики всегда сопровождается ее блокированием. Это означает, что каталоги dbms_stats.relation_stats_locked и dbms_stats.column_stats_locked заполняются данными из выбранной резервной копии.

Выполнить восстановление можно с помощью функций различного уровня детализации:

  • Восстановление всей базы данных:

    SELECT dbms_stats.restore_database_stats('2025-08-25 21:27:02');

    Восстановление выполняется по дате создания резервной копии.

    примечание

    Указанное время сравнивается со значением поля time в представлении dbms_stats.backup_history. Необходимо, чтобы переданное время было равно или больше значения в time.

  • Восстановление статистики схемы:

    SELECT dbms_stats.restore_schema_stats('ext', '2025-08-25 21:27:02');

    Восстанавливает статистику для схемы ext на указанный момент времени.

  • Восстановление статистики таблицы:

    SELECT dbms_stats.restore_table_stats('s0.st0', '2012-02-29 23:59:57');
    SELECT dbms_stats.restore_table_stats('s0.st0', '2012-02-29 23:59:57.000002');
    SELECT dbms_stats.restore_table_stats('s0.st0', '2012-01-01 00:00:00');

    Позволяет восстановить статистику для конкретной таблицы на момент времени.

    Также можно указать схему отдельно:

    SELECT dbms_stats.restore_table_stats('s0', 'st0', '2012-02-29 23:59:57');
  • Восстановление статистики отдельного столбца:

    SELECT dbms_stats.restore_column_stats('s0.st0', 'id', '2012-02-29 23:59:57');
    SELECT dbms_stats.restore_column_stats('s0.st0', 'id', '2012-02-29 23:59:57.000002');
    SELECT dbms_stats.restore_column_stats('s0.st0', 'id', '2012-01-01 00:00:00');

    Позволяет восстановить статистику по конкретному столбцу указанной таблицы.

  • Восстановление по индексу резервной копии:

    SELECT dbms_stats.restore_stats(2);

    Восстановление выполняется по индексу резервной копии из представления dbms_stats.backup_history.

Копирование статистики между объектами

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

Поддерживаются следующие варианты копирования:

  • между таблицей и ее секцией;
  • между секциями;
  • между индексами;
  • между таблицей и индексом.

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

Синтаксис функции:

CREATE OR REPLACE FUNCTION dbms_stats.copy_table_stats(
schemaname_src text default '', -- исходная схема
relation_src text default '', -- исходный объект статистики
schemaname_dst text default '', -- целевая схема
relation_dst text default '', -- целевой объект статистики
purge_existing_plan bool default false, -- инвалидация ранее загруженного контекста
backup_id_src int8 default 0 -- индекс резервной копии (> 0 — по ID, 0 — последняя копия)
);

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

Внимание!

Для выполнения копирования должна существовать резервная копия статистики исходного объекта.

Пример резервного копирования и копирования статистики:

-- создание резервной копии статистики таблицы
SELECT dbms_stats.backup_table_stats('ext','table_part_volume_1','1 backup');

-- копирование статистики между партициями таблицы
SELECT dbms_stats.copy_table_stats(
'ext','table_part_volume_1',
'ext','table_part_volume_3',
false,0
);

Если индекс резервной копии указывается 0, то выполняется копирование последней резервной копии исходного объекта. При значении индекса больше 0, происходит восстановление резервной копии с этим номером, например:

SELECT dbms_stats.copy_table_stats(
'ext','table_part_volume_1',
'ext','table_part_volume_3',
false,18
);

Копирование выполняется из резервной копии с идентификатором 18.

Флаг purge_existing_plan определяет поведение планировщика при замене статистики.

При использовании расширения и при условии блокировки статистики (через lock или copy) создается контекст статистики в хеш-таблице хранения статистики. При непрерывной работе базы данных, дальнейший разбор статистики не выполняется, используется существующий контекст. Контекст статистики связан только с OID объекта, по которому выполняется SQL.

Если purge_existing_plan = true:

  • старый контекст удаляется из хеш-таблицы;
  • создается новый контекст;
  • план выполнения обновляется немедленно, если новая статистика объективно может повлиять на выбор плана.

Если purge_existing_plan = false, то до перезапуска кластера используется старая статистика, загруженная из каталогов dbms_stats.relation_stats_locked и dbms_stats.column_stats_locked.

Удобный вариант применения копирования — перенос статистики между партициями партиционированной таблицы.

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

Экспорт и импорт статистики во внешнее хранилище

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

Функции экспорта статистики

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

Внимание!

Выгружается только заблокированная статистика — резервные копии не сохраняются во внешний файл.

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

-- Уровень базы данных
SELECT dbms_stats.export_database_stats('/tmp/export_db_stats.dmp');

-- Уровень схемы
SELECT dbms_stats.export_schema_stats('ext', '/tmp/export_schema_stats.dmp');

-- Уровень таблицы
SELECT dbms_stats.export_table_stats('ext', 'table_part_volume_3', '/tmp/export_table_stats.dmp');

-- Уровень атрибута таблицы
SELECT dbms_stats.export_column_stats('ext.table_part_volume_3', 'id', '/tmp/export_column_stats.dmp');

Функции импорта статистики

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

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

-- Уровень базы данных
SELECT dbms_stats.import_database_stats('/tmp/export_db_stats.dmp');

-- Уровень схемы
SELECT dbms_stats.import_schema_stats('ext', '/tmp/export_schema_stats.dmp');

-- Уровень таблицы
SELECT dbms_stats.import_table_stats('ext', 'table_part_volume_3', '/tmp/export_stats.dmp');

-- Уровень атрибута таблицы
SELECT dbms_stats.import_column_stats('ext.table_part_volume_1', 'id', '/tmp/export_column_stats.dmp');

Очистка статистики

  1. Удаление истории резервирования.

    Удаление записей о создании резервных копий выполняется функцией:

    CREATE FUNCTION dbms_stats.purge_stats(
    backup_id int8,
    force bool DEFAULT false
    );

    По умолчанию удаляется история резервирования уровня базы данных. При force = true удаляются все записи с индексом, равным указанному backup_id.

  2. Инициализация.

    Для очистки всех структур каталога расширения используется функция:

    SELECT dbms_stats.reinit();

    После ее выполнения каталог pg_dbms_stats готов к работе.

    Альтернативно можно вызвать:

    SELECT dbms_stats.clean_up_stats();

    Обе функции (reinit и clean_up_stats) равнозначны по назначению и позволяют сбросить структуры расширения в исходное состояние.