AI PostgreSQL 19: Часть 4 или Коммитфест 2026-01

  • Автор темы Автор темы AI
  • Дата начала Дата начала

AI

Команда форума
Редактор
Регистрация
23 Авг 2023
Сообщения
3,969
Реакции
0
Баллы
36
Ofline
y3n_gcqxozxageyzovbriho5wgk.png


Продолжаем цикл статей с обзором изменений 19 версии. Сегодня о январском коммитфесте 2026 года.

Самое интересное из предыдущих коммитфестов можно прочитать здесь: 2025-07, 2025-09, 2025-11.


Секционирование: слияние и разделение секций
pg_dump[all]/pg_restore: выгрузка и восстановление расширенной статистики
file_fdw: пропуск нескольких начальных строк
Логическая репликация: включение и отключение логического декодирования WAL без перезапуска сервера
Мониторинг задержек синхронизации слотов логической репликации
pg_available_extensions показывает каталог установки расширения
Новая функция pg_get_multixact_stats: статистика использования мультитранзакций
Улучшения в мониторинге выполнения очистки и анализа
Очистка: информация об использовании памяти
vacuumdb --dry-run
Оптимизация jsonb_agg
Оптимизация LISTEN/NOTIFY
ICU: оптимизация функций преобразования символов
Параметр standard_conforming_strings больше нельзя отключить


Секционирование: слияние и разделение секций
commit: f2e4cc42795, 4b3d173629f

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

Предположим, что секционированная таблица бронирований авиабилетов разбита на секции по месяцам. Сейчас в ней три секции за последние три месяца прошлого года:

SELECT tableoid::regclass AS partition, count(*)
FROM bookings_range
GROUP BY ALL;


partition | count
-----------+--------
p2025_10 | 434287
p2025_11 | 410680
p2025_12 | 411025
(3 rows)


Если данные за прошлый год используются нечасто, их можно схлопнуть в одну секцию:

ALTER TABLE bookings_range
MERGE PARTITIONS (
p2025_10,
p2025_11,
p2025_12
)
INTO p2025;

SELECT tableoid::regclass AS partition, count(*)
FROM bookings_range
GROUP BY ALL;


partition | count
-----------+---------
p2025 | 1255992
(1 row)


К разбиению по месяцам можно вернуться обратной командой:

ALTER TABLE bookings_range
SPLIT PARTITION p2025
INTO (
PARTITION p2025_10 FOR VALUES FROM ('2025-10-01') TO ('2025-11-01'),
PARTITION p2025_11 FOR VALUES FROM ('2025-11-01') TO ('2025-12-01'),
PARTITION p2025_12 FOR VALUES FROM ('2025-12-01') TO ('2026-01-01')
);

SELECT tableoid::regclass AS partition, count(*)
FROM bookings_range
GROUP BY ALL;


partition | count
-----------+--------
p2025_10 | 434287
p2025_11 | 410680
p2025_12 | 411025
(3 rows)


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

pg_dump[all]/pg_restore: выгрузка и восстановление расширенной статистики
commit: d756fa1019f, 0e80f3f88de, c32fb29e979, 302879bd68d, efbebb4e858

В 18-й версии pg_dump и pg_dumpall научились выгружать базовую статистику таблиц и индексов. А psql и pg_restore — ее восстанавливать. Теперь пришел черед всех видов расширенной статистики, включая статистику по выражениям.

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

CREATE TABLE test AS SELECT 1 AS id, 'a' AS descr;

CREATE STATISTICS test_stats (ndistinct, dependencies, mcv)
ON id, descr
FROM test;
ANALYZE test;


Расширенная статистика выгружается при помощи новой функции pg_restore_extended_stats, вызов которой можно найти в выводе pg_dump:

$ pg_dump --statistics-only| grep -A 12 pg_restore_extended_stats


SELECT * FROM pg_catalog.pg_restore_extended_stats(
'version', '190000'::integer,
'schemaname', 'public',
'relname', 'test',
'statistics_schemaname', 'public',
'statistics_name', 'test_stats',
'inherited', 'f'::boolean,
'n_distinct', '[{"attributes": [1, 2], "ndistinct": 1}]':😛g_ndistinct,
'dependencies', '[{"attributes": [1], "dependency": 2, "degree": 1.000000}, {"attributes": [2], "dependency": 1, "degree": 1.000000}]':😛g_dependencies,
'most_common_vals', '{{1,a}}'::text[],
'most_common_freqs', '{1}'::double precision[],
'most_common_base_freqs', '{1}'::double precision[]
);


file_fdw: пропуск нескольких начальных строк
commit: 26cb14aea12

В статье об июльском коммитфесте 19-й версии говорилось о том, что при загрузке строк в таблицу командой COPY можно указать, сколько начальных строк пропустить, прежде чем начнутся данные. Теперь такая же возможность появилась в file_fdw.

Логическая репликация: включение и отключение логического декодирования WAL без перезапуска сервера
commit: 67c20979ce7

Для работы логической репликации требуется, чтобы в WAL записывалась информация, необходимая для логического декодирования. При значении параметра wal_level по умолчанию (replica) эта информация не пишется. А изменение параметра требует перезапуска сервера.

Теперь уровень записи в WAL будет определяться динамически, в зависимости от наличия слотов логической репликации. Как только появляется первый слот, фактический уровень журнала повышается до logical. И наоборот, после удаления последнего слота фактический уровень понизится до replica. Текущее значение фактического уровня журнала показывает новый параметр effective_wal_level, доступный только для чтения.

\dconfig+ *wal_level


List of configuration parameters
Parameter | Value | Type | Context | Access privileges
---------------------+---------+------+------------+----------------------
effective_wal_level | replica | enum | internal |
wal_level | replica | enum | postmaster | postgres=sA/postgres+
| | | | alice=sA/postgres
(2 rows)


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

SELECT pg_create_logical_replication_slot('slot', 'pgoutput');


pg_create_logical_replication_slot
------------------------------------
(slot,2/4BB9C340)
(1 row)


\dconfig *wal_level


List of configuration parameters
Parameter | Value
---------------------+---------
effective_wal_level | logical
wal_level | replica
(2 rows)


Удаляем единственный слот — и ближайшая контрольная точка снизит фактический уровень журнала:

SELECT pg_drop_replication_slot('slot');
CHECKPOINT;

\dconfig *wal_level


List of configuration parameters
Parameter | Value
---------------------+---------
effective_wal_level | replica
wal_level | replica
(2 rows)


Мониторинг задержек синхронизации слотов логической репликации
commit: 76b78721ca, e68b6adad96, 5db6a344abc

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

А в представлении pg_stat_replication_slots появились два новых столбца slotsync_skip_count и slotsync_last_skip, показывающие, сколько раз синхронизация слотов была пропущена и когда это случилось в последний раз.

pg_available_extensions показывает каталог установки расширения
commit: f3c9e341cdf

В представления pg_available_extensions и pg_available_extension_versions добавлен столбец location, показывающий каталог, где установлено расширение.

SELECT name, location
FROM pg_available_extensions
LIMIT 5;


name | location
------------------+----------
pg_surgery | $system
dict_int | $system
plpython3u | $system
seg | $system
ltree_plpython3u | $system
(5 rows)


Значение $system — это каталог по умолчанию для системы.

Новая функция pg_get_multixact_stats: статистика использования мультитранзакций
commit: 97b101776ce

Заблокируем одну и ту же строку в режиме KEY SHARE в двух сеансах:

1=# BEGIN;
1=*# SELECT * FROM bookings WHERE book_ref = '2EW1SQ' FOR KEY SHARE;


book_ref | book_date | total_amount
----------+-------------------------------+--------------
2EW1SQ | 2025-09-01 03:00:12.557744+03 | 8125.00
(1 row)


2=# BEGIN;
2=*# SELECT * FROM bookings WHERE book_ref = '2EW1SQ' FOR KEY SHARE;


book_ref | book_date | total_amount
----------+-------------------------------+--------------
2EW1SQ | 2025-09-01 03:00:12.557744+03 | 8125.00
(1 row)


Две транзакции в этих сеансах используют мультитранзакцию для удержания разделяемой блокировки на строку. Функция pg_get_multixact_stats покажет агрегированную статистику использования мультитранзакций:

SELECT *
FROM pg_get_multixact_stats()
\gx


-[ RECORD 1 ]----+--
num_mxids | 1
num_members | 2
members_size | 0
oldest_multixact | 1


Эта статистика включает:


  • общее количество мультитранзакций, присутствующих сейчас в системе;


  • общее количество членов (транзакций) этих мультиранзакций;


  • объем памяти (в байтах), требуемый для хранения информации в каталоге pg_multixact/members;


  • самый старый номер используемой сейчас мультитранзакции.

Улучшения в мониторинге выполнения очистки и анализа
commit: 0d789520619, ab40db3852d

В представление pg_stat_progress_vacuum добавлены два столбца:


  • mode с возможными значениями normal, aggressive и failsafe;


  • started_by — manual, autovacuum и autovacuum_wraparound.

Столбец started_by появился и в представлении pg_stat_progress_analyze. Здесь у него два варианта значения: manual и autovacuum.

Очистка: информация об использовании памяти
commit: 736f754eed0

Команда VACUUM с параметром verbose выводит новую строку (memory usage) с информацией о том, сколько всего памяти потребовалось для хранения идентификаторов мертвых строк, сколько раз (resets) список идентификаторов не удалось поместить в отведенный лимит памяти (maintenance_work_mem).

VACUUM(verbose) bookings;


INFO: vacuuming "demo.bookings.bookings"

memory usage: dead item storage 0.02 MB accumulated across 0 resets (limit 64.00 MB each)

VACUUM


Эта же строка появится в журнале сервера, если включено журналирование автоочистки.

Стоит отметить, в 17-й версии для хранения списка идентификаторов мертвых строк процесс очистки стал использовать сжатое префиксное дерево (radix tree) вместо обычного массива. Благодаря этой прекрасной оптимизации получить значение resets больше 0 стало очень сложно — даже для целей тестирования. 🙂

vacuumdb --dry-run
commit: d107176d27c

По аналогии с некоторыми утилитами сервера, vacuumdb получила параметр --dry-run. С этим параметром утилита покажет, какие команды очистки и анализа предполагается отправить на сервер.

Создадим таблицу в схеме public и посмотрим, что vacuumdb посчитает нужным выполнить для этой схемы:

$ psql -d postgres -c 'CREATE TABLE public.test (id int)'


$ vacuumdb -d postgres -n public --dry-run


vacuumdb: Executing in dry-run mode.
No commands will be sent to the server.
vacuumdb: vacuuming database "postgres"
VACUUM (SKIP_DATABASE_STATS) public.test;
VACUUM (ONLY_DATABASE_STATS);


Оптимизация jsonb_agg
commit: 0986e95161c, b61aa76e458, 005a2907dc3

Функция jsonb_agg стала значительно быстрее. Особенно при работе с типами integer и numeric. Простой замер времени выполнения в 18-й и 19-й версиях:

18=# \timing
18=# SELECT jsonb_agg(g.x) FROM generate_series(1.0,1000000) AS g(x)\g /dev/null


Time: 677,667 ms


В 19-й версии:

19=# SELECT jsonb_agg(g.x) FROM generate_series(1.0,1000000) AS g(x)\g /dev/null


Time: 374,138 ms


Речь об ускорении именно jsonb_agg, просто json_agg и без этой оптимизации работает примерно с такой же скоростью.

Оптимизация LISTEN/NOTIFY
commit: 282b1cde9de

Механизм LISTEN/NOTIFY хорошо работает в ситуации, когда несколько получателей отслеживают уведомления из одного канала. Но производительность механизма страдает, если разные получатели ждут уведомлений из разных каналов. Процесс NOTIFY пытается доставить уведомление получателям всех каналов, а не только тем, кто подписался на канал.

В 19-й версии механизм LISTEN/NOTIFY поддерживает хеш-таблицу каналов и получателей, что позволило в разы увеличить производительность отправки уведомлений.

ICU: оптимизация функций преобразования символов
commit: c4ff35f1044

В базах данных с кодировкой UTF8 оптимизированы функции преобразования регистра символов с правилами сортировки ICU.

18=# SELECT count(upper(passenger_name COLLATE "ru-x-icu"))
FROM tickets;


count
----------
10836563
(1 row)

Time: 3107,008 ms (00:03,107)


В 19-й версии этот же запрос выполняется существенно быстрее:

19=# SELECT count(upper(passenger_name COLLATE "ru-x-icu"))
FROM tickets;


count
----------
10836563
(1 row)

Time: 1815,985 ms (00:01,816)


Параметр standard_conforming_strings больше нельзя отключить
commit: 45762084545

Параметр standard_conforming_strings больше нельзя отключить. А связанный параметр escape_string_warning удален.

Поведение в 18-й версии:

SET standard_conforming_strings = off;
SET escape_string_warning = off;
SELECT 'A\nB';


?column?
----------
A +
B
(1 row)


Т.е. в строке с одинарными кавычками символ \ воспринимается как специальный.

В 19-й версии такого поведения больше не будет:

SET standard_conforming_strings = off;


ERROR: non-standard string literals are not supported


SET escape_string_warning = off;


ERROR: unrecognized configuration parameter "escape_string_warning"


SELECT 'A\nB';


?column?
----------
A\nB
(1 row)


А для использования специальных символов перед строковой константой нужно ставить E:

SELECT E'A\nB';


?column?
----------
A +
B
(1 row)


Можно было бы спокойно пройти мимо этого патча, если бы не 1C. Для работы этой популярной в России (и не только) системы рекомендуется отключить standard_conforming_strings. Вероятно для перехода на 19-ю версию разработчикам 1С и СУБД на основе PostgreSQL придется протестировать работу со строками с учетом этих изменений.


На этом пока всё. Впереди события пятого мартовского коммитфеста 19-й версии.
 
Назад
Сверху Снизу
Яндекс.Метрика Рейтинг@Mail.ru