PostgreSQL 14: Часть 1 или «июльский разогрев» (Коммитфест 2020-07)

?v=1

Выход PostgreSQL 13, о возможностях которого мы уже писали, планируется только осенью. Ничего принципиально нового в нем уже не появится. Поэтому самое время перейти к PostgreSQL 14.

Жизненный цикл нововведений 14 версии состоит из 5 коммитфестов. Первый из которых — июльский — уже завершился, а значит есть что обсудить.
Принятых изменений было намного больше, чем будет описано. Рассмотрены в основном патчи с новым или измененным функционалом СУБД. Именно такие изменения представляют наибольший интерес для пользователей PostgreSQL. А то что не попало, как правило, относится к следующим категорям: исправления ошибок, рефакторинг, инфраструктура проекта, документация (почти всегда), комментирование кода, тестирование.

Да и вообще, цель описать абсолютно всё не ставилась. Отслеживание изменений в точности — очень трудоемкий процесс. Поэтому к решению важных задач, таких как переход на новую версию, всегда нужно готовиться по замечаниям к выпуску. А сейчас можно просто расслабиться и почитать вести с «полей разработки». Если появится желание самостоятельно попробовать новинки, то придется собирать PostgreSQL из исходных кодов. А вот документация на принятые патчи доступна онлайн.

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

Итак, начинаем.


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

pg_stat_activity.leader_pid IS NULL для ведущего процесса
commit: 11a68e

После заморозки кода 13 версии изменились правила заполнения нового столбца leader_pid в pg_stat_activity — номер ведущего процесса при выполнении параллельных запросов. Первоначально предполагалось заполнять этот столбец не только для всех рабочих процессов, участвующих в выполнении запроса, но и для самого ведущего процесса. Теперь у ведущего процесса leader_pid будет оставаться незаполненным.

Документация: новое форматирование таблиц в главе «Функции и операторы»
commit: e894c61

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

Параметра wal_keep_segments больше нет
commit: c3fe108

Вместо него wal_keep_size. Теперь нужно оперировать не количеством сегментов в pg_wal, а их размером. Сделано для унификации с новым для 13 версии параметром max_slot_wal_keep_size — максимальный размер файлов WAL, сохраняемых для слотов репликации.

Мимо этого изменения не смог пройти depesz в своей новой серии Waiting for PostgreSQL 14. Но изменение попадет и в 13 версию, начиная с beta3.

pg_validatebackup → pg_verifybackup
commit: dbc60c5

Новая для 13 версии утилита pg_validatebackup после заморозки кода переименована и будет называться pg_verifybackup.

sslminprotocolversion → ssl_min_protocol_version, sslmaxprotocolversion → ssl_max_protocol_version
commit: 401aad6

Новые для 13 версии параметры sslminprotocolversion и sslmaxprotocolversion переименованы: ssl_min_protocol_version и ssl_max_protocol_version.

ALTER… NO DEPENDS ON EXTENSION …
commit: 5fc7039

В версии 9.6 добавили команду ALTER… DEPENDS ON EXTENSION… для указания, что объект зависит от расширения. Но забыли реализовать обратный вариант команды на снятие зависимости. Упущение исправлено и портировано в 13 версию. Из-за отсутствия жалоб портировать в более ранние версии не стали.

Новый параметр hash_mem_multiplier
commit: d6c08e2

Возможность не выходить за установленные пределы оперативной памяти (work_mem) при создании хеш-таблицы появилась еще на момент заморозки кода 13 версии. Если на этапе выполнения запроса окажется, что планировщик ошибся в оценках и work_mem не хватает — будут использоваться временные файлы. Это хорошо, т.к. нынешнее поведение — использовать столько памяти, сколько потребуется, а это не всегда приятное соревнование с OOM-killer.

Для управления поведением планировщика предлагались параметры enable_groupingsets_hash_disk и enable_hashagg_disk. К моменту выпуска 13 beta2 оба параметра заменили на hashagg_avoid_disk_plan. Но и этого оказалось недостаточно. Теперь вместо hashagg_avoid_disk_plan появился hash_mem_multiplier.

Название взято по аналогии с bgwriter_lru_multiplier и представляет собой множитель, на который планировщик умножает work_mem, чтобы получить объем памяти, который может использоваться для операции хеширования. Значение по умолчанию 1, что не меняет предыдущего поведения — память для хеширования не должна превышать work_mem.

Управляя этим параметром, и не меняя work_mem, можно дать планировщику больше шансов выбрать варианты с хеш вместо менее требовательных к наличию свободной памяти сортировок (в частности hash join вместо merge join).

EXPLAIN ANALYZE: изменения в формате вывода узла HashAggregate
commit: 0e3e1c4

А вот как будут выглядеть операции хеширования в плане запроса. Посмотрим на примере такого запроса к демо-базе:

EXPLAIN (ANALYZE) SELECT tf.fare_conditions, count(*)
                    FROM ticket_flights tf GROUP BY tf.fare_conditions;


Ограничимся лишь фрагментом плана с узлом HashAggregate.

Версия 12:

->  Partial HashAggregate
      Group Key: fare_conditions
      ->  Parallel Seq Scan on ticket_flights tf


Версия 13beta2:

->  Partial HashAggregate
      Group Key: fare_conditions
      Peak Memory Usage: 24 kB
      Worker 0:  Peak Memory Usage: 24 kB
      Worker 1:  Peak Memory Usage: 24 kB
      ->  Parallel Seq Scan on ticket_flights tf


Теперь:

->  Partial HashAggregate
      Group Key: fare_conditions
      Batches: 1  Memory Usage: 24kB
      Worker 0:  Batches: 1  Memory Usage: 24kB
      Worker 1:  Batches: 1  Memory Usage: 24kB
      ->  Parallel Seq Scan on ticket_flights tf


Чтобы не играть в игру «найди 10 отличий» вот они, и их 5:

  • счетчик пакетов (batches) начинается с 1, а не 0;
  • Batches выводится всегда, даже если пакет был всего 1;
  • для текстового формата выводится Batches вместо HashAgg Batches;
  • для текстового формата выводится Memory Usage вместо Peak Memory Usage;
  • Batches выводится перед Memory Usage


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

Отслеживание пауз в работе очистки
commit: cedffbd

Паузы в работе очистки ([autovacuum_]vacuum_cost_delay) теперь отображаются в pg_stat_activity.wait_event_type=Timeout и wait_event=VacuumDelay. До этого считалось что процесс ничего не ждет и столбцы wait_enent* были не заполнены.

Хватит о 13 версии, начиная со следующего раздела переходим к эксклюзиву версии 14.


vacuumdb --no-truncate --no-index-cleanup
commit: 9550ea3

Утилита vacuumdb получила два новых аргумента командной строки: --no-truncate и --no-index-cleanup. С их помощью можно отключать соответствующие параметры VACUUM, появившиеся в 12 версии: TRUNCATE (нужно ли уменьшать файл, если последние страницы полностью очищены) и INDEX_CLEANUP (нужно ли выполнять очистку индексов).


pg_stat_statements: отслеживание количества обработанных строк у некоторых служебных команд
commit: 6023b7e

Раньше для команд CREATE TABLE AS, SELECT INTO, CREATE MATERIALIZED VIEW, FETCH в pg_stat_statements.rows не фиксировалось количество обработанных записей. Теперь эта информация стала доступна:

CREATE TABLE bookings_copy AS SELECT * FROM bookings;
SELECT 262788
SELECT query,rows FROM pg_stat_statements 
    WHERE query LIKE 'CREATE TABLE bookings_copy%';
                        query                         |  rows  
------------------------------------------------------+--------
 CREATE TABLE bookings_copy AS SELECT * FROM bookings | 262788


Новые столбцы в pg_prepared_statements: generic_plans, custom_plans
commit: d05b172

В pg_prepared_statements появилась информация о количестве запросов, выполненных с общим и частным планом. Следующая цель — добавить такую же информацию в pg_stat_statements.

По умолчанию (plan_cache_mode = auto), если после пяти планирований запроса планировщик видит, что общий план будет не хуже частного, то он переходит на общий план, минимизируя свою работу. В следующем примере подготовленный запрос выполняется семь раз и после пятого повторения последние два раза выполняются уже с общим планом:

\d pg_prepared_statements
                  View "pg_catalog.pg_prepared_statements"
     Column      |           Type           | Collation | Nullable | Default
-----------------+--------------------------+-----------+----------+---------
 name            | text                     |           |          |
 statement       | text                     |           |          |
 prepare_time    | timestamp with time zone |           |          |
 parameter_types | regtype[]                |           |          |
 from_sql        | boolean                  |           |          |
 generic_plans   | bigint                   |           |          |
 custom_plans    | bigint                   |           |          |
PREPARE q(text) AS SELECT count(*) FROM bookings WHERE book_ref = $1;
SELECT $$EXECUTE q('ABCDEF')$$ FROM generate_series(1,7)\gexec

SELECT generic_plans, custom_plans FROM pg_prepared_statements WHERE name = 'q';
 generic_plans | custom_plans
---------------+--------------
             2 |            5


Доступ не суперпользователям к функциям, работающим с источниками репликации
commit: cc07264

Функциями, работающим с источниками репликации (pg_*replication_origin*), теперь могут пользоваться не только суперпользователи. Ранее проверка на суперпользователя была жестко зашита в коде. Проверку убрали и отобрали права на исполнение у public. Теперь суперпользователь может командой GRANT выдать разрешение на выполнение этих функций любым пользователям. Это в том числе дает возможность запрашивать данные из представления pg_replication_origin_status обычным пользователям.

В начале переписки рассматривалась идея предоставить роли pg_monitor доступ к pg_replication_origin_status. Но это в настоящий момент не реализовано.

Функция pgstattuple_approx поддерживает таблицы TOAST
commit: ee0202d

Функция pgstattuple_approx предоставляет приблизительную, но более быструю, альтернативу функции pgstattuple из одноименного расширения для оценки распухания объектов базы данных. Ускорение обеспечивается тем, что таблица сканируется не целиком, а с учетом карты видимости и карты свободного пространства. До 14 версии примерную оценку нельзя было получить для таблиц TOAST. Теперь это исправлено.


У этой группы патчей есть очень хорошая особенность. Как правило, для получения лучшего быстродействия ничего не нужно делать. Просто переходим на новую версию и всё начинает работать быстрее. Хотя для некоторых изменений предусмотрены «ручки» (параметры), которые нужно научиться аккуратно подкручивать для получения наилучшего результата.

Кажется, что для описания увеличения производительности хорошо бы привести результаты замеров. Но их не будет. Во-первых, это достаточно сложная работа, требующая очень тщательной подготовки, разработки методики и описания условий проведения тестов. А во-вторых, какие бы впечатляющие результаты не приводились в замерах, они всё равно получены на некой абстрактной системе. И полностью доверять им не стоит. Любую оптимизацию нужно проверять на своей собственной системе и нагрузке. Поэтому поверим разработчикам PostgreSQL, которые на своих тестах получили положительные результаты.

Оптимизация добавления записей в каталоги pg_attribute и pg_shdepend
commit: e3931d0

Добавление набора записей в pg_attribute, например при создании таблицы, теперь выполняется за один раз. По словам разработчиков, в некоторых случаях это приводит к 15% сокращению записи в WAL. Такая же оптимизация для pg_shdepend.

Оптимизировано выполнение операторов PL/pgSQL
commit: 1f902d4, fe2e206

PL/pgSQL стал чуть быстрее. Правда, это не особенно заметно на архитектуре x86_64, а вот aarch64 выиграла однозначно.

Оптимизация RETURN QUERY в PL/pgSQL
commit: 2f48ede

Оптимизирована не только выдача результата запроса из функции, но и добавлена возможность параллельного выполнения запросов в RETURN QUERY, RETURN QUERY EXECUTE.

Создадим функцию с оператором RETURN QUERY.

CREATE FUNCTION get_fc(OUT fare_conditions varchar, OUT total bigint)
    RETURNS SETOF RECORD AS $$
BEGIN
    RETURN QUERY SELECT tf.fare_conditions, count(*)
                   FROM ticket_flights tf GROUP BY tf.fare_conditions;
END;
$$ LANGUAGE plpgsql STABLE PARALLEL SAFE;


Подключим auto_explain для получения плана запроса и выполним функцию:

SET auto_explain.log_min_duration = 0;
SET auto_explain.log_nested_statements = on;
SELECT * FROM get_fc();


В журнале видим, что использовался план с параллельным выполнением:

\! tail -n18 logfile
2020-08-06 23:23:34.825 MSK [17278] LOG:  duration: 80.701 ms  plan:
    Query Text: SELECT tf.fare_conditions, count(*)
                       FROM ticket_flights tf GROUP BY tf.fare_conditions
    Finalize GroupAggregate  (cost=16250.86..16251.62 rows=3 width=16)
      Group Key: fare_conditions
      ->  Gather Merge  (cost=16250.86..16251.56 rows=6 width=16)
            Workers Planned: 2
            ->  Sort  (cost=15250.84..15250.85 rows=3 width=16)
                  Sort Key: fare_conditions
                  ->  Partial HashAggregate  (cost=15250.79..15250.82 rows=3 width=16)
                        Group Key: fare_conditions
                        ->  Parallel Seq Scan on ticket_flights tf  (cost=0.00..13072.19 rows=435719 width=8)
2020-08-06 23:23:34.825 MSK [17278] CONTEXT:  SQL statement "SELECT tf.fare_conditions, count(*)
                       FROM ticket_flights tf GROUP BY tf.fare_conditions"
    PL/pgSQL function get_fc() line 3 at RETURN QUERY
2020-08-06 23:23:34.825 MSK [17278] LOG:  duration: 82.101 ms  plan:
    Query Text: SELECT * FROM get_fc();
    Function Scan on get_fc  (cost=0.25..10.25 rows=1000 width=40)


Выделение «быстрой» динамической разделяемой памяти для параллельных запросов
commit: 84b1c63

Когда для общей разделяемой памяти используются огромные страницы, новым параметром min_dynamic_shared_memory при запуске сервера можно выделить область динамической разделяемой памяти для параллельно выполняемых запросов. Используя эту «быструю» память, параллельные запросы выполняются существенно эффективнее. Если этой области памяти им не хватит, то будет выделяться дополнительная память способом указанным в dynamic_shared_memory_type, но ускорения уже не получится.

Параллельные запросы: данные в узле Gather обрабатываются более эффективно
commit: cdc7169

И этим всё сказано. Никаких настроек, параллельные запросы станут работать быстрее.

Все функции в расширении btree_gist помечены как PARALLEL_SAFE
commit: d98c08c

Теперь запросы, использующие операторы этого расширения, могут выполняться в параллельном режиме.

Оптимизация ввода-вывода для параллельного последовательного сканирования
commit: 56788d2

Раньше рабочие процессы получали на обработку по одному блоку за раз. Теперь получают порциями, размер которых зависит от размера таблицы. Тестирование патча показало хорошие результаты на дисках SSD для операционных систем Linux, FreeBSD, Windows. Однако для облачных провайдеров результаты не столь очевидны.

Кеширование размера файлов отношений при восстановлении
commit: c5315f4

При восстановлении, за счет кеширования, существенно сократилось количество вызовов, вычисляющих размер файлов вызовов lseek (SEEK_END). А это сокращение дисковых операций. Реплики работают в режиме восстановления только в один процесс. Любая оптимизация здесь всегда к месту.

COPY FROM в двоичном формате стала быстрее
commit: 0a0727c

Увеличена производительность загрузки данных командой COPY FROM в двоичном формате. В частности простые тесты показали ускорение на 30% в Linux, еще больше в MacOS.

COPY FROM в двоичном формате расходует меньше памяти
commit: cd22d3c

Еще одно небольшое улучшение для COPY FROM в двоичном формате позволяет сэкономить ~ 65Кб оперативной памяти на запуск.


Логическая репликация сможет передавать данные в двоичном формате
commit: 9de77b5

Это изменение делает логическую репликацию быстрее. При создании/изменении подписки можно запросить у сервера публикации передачу в двоичном формате:

CREATE|ALTER SUBSCRIPTION … WITH (BINARY = ON)


По умолчанию двоичный формат отключен. После включения двоичные данные будут передаваться для тех типов, у которых определены функции send/receive. В противном случае используются функции указанные для input/output.
Системные функции получения информации и адресации объектов возвращают NULL для несуществующих объектов
commit: 2a10fdc

В предыдущих версиях вызов подобных функций приводил к ошибке:

SELECT pg_describe_object('pg_class'::regclass, 0::oid, 0);
ERROR:  cache lookup failed for relation 0


Но ошибка cache lookup failed предназначена для внутреннего использования и не должна быть видна пользователям. Все видимые типы ошибок описаны в документации. Теперь запрос возвращает NULL.

Изменение будет полезно тем, кто пишет собственные инструменты/скрипты для работы с системным каталогом.

Управление размером огромных страниц
commit: d2bddc2

Новый параметр huge_page_size для управления размером огромных страниц. По умолчанию 0, размер огромных страниц определяется настройками ОС.

У типа numeric появились значения «бесконечность» и «минус бесконечность»
commit: a57d312

Поведение такое же как и у соответствующих значений для типов с плавающей точкой. Это дает возможность конвертировать значения с плавающей точкой (и интерфейсы их использующие) в числа с произвольной точностью.


Добавлен метод amadjustmembers для проверки членов класса операторов
commit: 9f96827

Началось всё с того, что в расширении intarray оказалось невозможным средствами SQL удалить оператор <@ из класса операторов для GiST. В результате был доработан интерфейс для индексных методов доступа. В частности появилась функция amadjustmembers. Расширение intarray пока осталось без изменений.

Операторы + и − для типа pg_lsn
commit: 9bae7e4

Еще не успели попробовать появившиеся в 13 версии агрегатные функции min и max для типа данных pg_lsn, как в 14-ю версию добавили арифметические операторы для добавления/вычитания байтов:

SELECT w.lsn, w.lsn +1, w.lsn -1 FROM (SELECT pg_current_wal_lsn()) AS w(lsn);
    lsn    | ?column?  | ?column?  
-----------+-----------+-----------
 0/EF04B78 | 0/EF04B79 | 0/EF04B77


Получение информации об источнике репликации для транзакции
commit: b1e48bb

Источник репликации можно получить новой функцией pg_xact_commit_timestamp_origin. Эту же информацию добавили в pg_last_committed_xact.

Чтобы получить информацию о времени фиксации транзакции и источнике репликации должен быть включен параметр track_commit_timestamp:

SHOW track_commit_timestamp;
 track_commit_timestamp
------------------------
 on
SELECT pg_current_xact_id();
 pg_current_xact_id 
--------------------
                721
SELECT * FROM pg_xact_commit_timestamp_origin('721'::xid);
           timestamp           | roident
-------------------------------+---------
 2020-08-10 13:57:18.103297+03 |       0
SELECT * FROM pg_last_committed_xact();
 xid |           timestamp           | roident
-----+-------------------------------+---------
 721 | 2020-08-10 13:57:18.103297+03 |       0


На этом пока всё. Продолжение следует после сентябрьского коммитфеста.

© Habrahabr.ru