pg_dbms_stats. Стабилизация планировщика запросов
Версия: 15.0.
В исходном дистрибутиве установлено по умолчанию: да.
Связанные компоненты: отсутствуют.
Схема размещения:
dbms_stats.
Расширение pg_dbms_stats позволяет управлять поведением планировщика за счет хранения таблиц статистики.
СУБД Pangolin управляет статистикой таблиц на основе выборочных значений таблиц и индексов с помощью команды ANALYZE. Оптимизатор запросов рассчитывает стоимость плана выполнения на основе этой статистики и выбирает план с наименьшей стоимостью. В таком случае неточные статистические данные или резкие изменения объема либо распределения данных могут привести к тому, что оптимизатор запросов выберет неожиданный или нежелательный план выполнения.
Расширение pg_dbms_stats предотвращает перепланирование запроса через механизм merge двух составляющих: реальной статистики pg_statistic и собственной статистики, ранее сохраненной во внутренних структурах расширения. Принцип работы расширения pg_dbms_stats заключается в подмене настоящей статистики на ранее сохраненную (заблокированную) в структурах хранения расширения.
pg_dbms_stats может фиксировать статистику для следующих объектов:
- таблицы;
- индексы (с ограничениями, кроме функциональных индексов);
- базы данных;
- столбцы.
Основная функциональность расширения представлена в таблице:
Команда | Описание |
|---|---|
| Создает резервную копию текущей статистики |
| Восстанавливает статистику из резервной копии и фиксирует ее |
| Удаляет более не нужные резервные копии |
| Фиксирует текущую статистику |
| Снимает фиксацию статистики |
| Освобождает фиксацию статистики (массово удаляет все неиспользуемые записи) |
| Выгружает статистику во внешний файл (бинарный формат) |
| Загружает статистику из внешнего файла и фиксирует ее |
Доработка
Не производилась.
Ограничения
Экспорт статистики через pg_dbms_stats доступен только для редакций Enterprise и Enterprise для ERP-систем.
Установка
Для начала использования расширения выполните следующие действия:
-
Создайте схему
dbms_stats:CREATE SCHEMA dbms_stats; -
Установите расширение в целевую БД:
CREATE EXTENSION pg_dbms_stats SCHEMA dbms_stats; -
Включите расширение через настройку параметра:
SET pg_dbms_stats.use_locked_stats TO on;примечаниеПри отключении расширения последующие планы запросов формируются без участия его кода.
Настройка
Таблицы
Следующая таблица содержит описание основных таблиц расширения pg_dbms_stats:
Имя таблицы | Описание |
|---|---|
| Содержит статистику на уровне таблиц для использования планировщиком запросов вместо стандартной статистики из |
| Содержит статистику на уровне столбцов для использования планировщиком запросов вместо стандартной статистики из |
| Хранит историю резервных копий статистики, включая идентификатор резервной копии и связанные с ней объекты |
| Содержит резервные копии статистики на уровне таблиц |
| Содержит резервные копии статистики на уровне столбцов |
Функции
Триггерные функции
Триггерные функции, которые используются для автоматического сброса кеша статистики таблиц и столбцов:
Имя функции | Описание |
|---|---|
| Триггерная функция для сброса кеша статистики таблицы |
| Триггерная функция для сброса кеша статистики столбца |
Резервирование статистики
Функции для создания резервных копий статистики различных объектов базы данных:
Имя функции | Описание |
|---|---|
| Основная функция резервного копирования статистики |
| Создает резервную копию статистики всей базы данных |
| Создает резервную копию статистики для схемы |
| Создает резервную копию статистики для таблицы |
| Создает резервную копию статистики для столбца |
Восстановление статистики
Функции для восстановления статистики из ранее созданных резервных копий и при необходимости фиксирование ее для планировщика:
Имя функции | Описание |
|---|---|
| Восстанавливает статистику всей базы данных из резервной копии и фиксирует ее |
| Восстанавливает статистику для схемы |
| Восстанавливает статистику для столбца |
| Восстанавливает выбранные резервные копии статистики |
Блокировка статистики
Функции для фиксации статистики, чтобы текущий план выполнения запросов оставался неизменным:
Имя функции | Описание |
|---|---|
| Блокирует статистику всей базы данных |
| Блокирует статистику схемы |
| Блокирует статистику таблицы |
| Блокирует статистику столбца |
Снятие блокировки статистики
Функции для снятия блокировки статистики, возвращающие планировщику возможность использовать реальные данные статистики:
Имя функции | Описание |
|---|---|
| Снимает блокировку статистики всей базы данных |
| Снимает блокировку статистики схемы |
| Снимает блокировку статистики таблицы |
| Снимает блокировку статистики столбца |
Импорт статистики
Функции для загрузки статистики из внешних файлов и ее фиксирование для использования планировщиком:
Имя функции | Описание |
|---|---|
| Импортирует статистику всей базы данных и фиксирует ее |
| Импортирует статистику схемы и фиксирует ее |
| Импортирует статистику таблицы и фиксирует ее |
| Импортирует статистику столбца и фиксирует ее |
Экспорт статистики
Функции для экспорта статистики во внешний файл для последующего импорта или анализа:
Функциональность доступна только для редакций Enterprise и Enterprise для ERP-систем.
Имя функции | Описание |
|---|---|
| Экспортирует статистику всей базы данных во внешний файл |
| Экспортирует статистику схемы во внешний файл |
| Экспортирует статистику таблицы во внешний файл |
| Экспортирует статистику столбца во внешний файл |
Очистка статистики
Функции для удаления устаревших или неиспользуемых резервных копий и заблокированных статистик:
Функция | Описание |
|---|---|
| Удаляет ненужные резервные копии статистики |
| Удаляет все неиспользуемые заблокированные статистики |
Копирование статистики
Функция 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');
Очистка статистики
-
Удаление истории резервирования.
Удаление записей о создании резервных копий выполняется функцией:
CREATE FUNCTION dbms_stats.purge_stats(
backup_id int8,
force bool DEFAULT false
);По умолчанию удаляется история резервирования уровня базы данных. При
force = trueудаляются все записи с индексом, равным указанномуbackup_id. -
Инициализация.
Для очистки всех структур каталога расширения используется функция:
SELECT dbms_stats.reinit();После ее выполнения каталог
pg_dbms_statsготов к работе.Альтернативно можно вызвать:
SELECT dbms_stats.clean_up_stats();Обе функции (
reinitиclean_up_stats) равнозначны по назначению и позволяют сбросить структуры расширения в исходное состояние.