PG12: Дюжина патчей от Postgres Professional

в 14:16, , рубрики: dbms, postgres, postgresql, базы данных, Блог компании Postgres Professional, СУБД

Приятно видеть знакомые фамилии в списке Acknowledgments официального релиза PostgreSQL 12. Мы решили свести вместе попавшие в релиз новшества и некоторые багфиксы, над которыми трудились наши разработчики.

1. Поддержка JSONPath

Release Notes это звучит как Add support for the SQL/JSON path language (Nikita Glukhov, Teodor Sigaev, Alexander Korotkov, Oleg Bartunov, Liudmila Mantrova)

Сам этот патч, возможности JSONPath и история вопроса обсуждались в деталях в отдельной статье здесь на харбре. JSONPath — серьезное достижение Postgres Professional и одно из главных новшеств PostgreSQL 12 вообще.

В 2014 году А.Коротковым, О.Бартуновым и Ф.Сигаевым было разработано расширение jsquery, вошедшее в результате в версию Postgres Pro Standard 9.5 (и в более поздние версии Standard и Enterprise). Оно дает дополнительные, очень широкие возможности для работы с json(b).

Когда появился стандарт SQL:2016, оказалось, что его семантика не так уж сильно отличается от нашей в расширении jsquery. Не исключено, что авторы стандарта даже поглядывали на jsquery, изобретая JSONPath. Нашей команде пришлось реализовывать немного по-другому то, что у нас уже было и, конечно, много нового тоже.

Хотя специальный патч с функциями до сих пор не закоммичен, в патче JSONPath уже есть ключевые функции для работы с JSON(B), например:

jsonb_path_query('{"a": [1,2,3,4,5]}', '$.a[*] ? (@ > 2)') возвращает 3, 4, 5
jsonb_path_query('{"a": [1,2,3,4,5]}', '$.a[*] ? (@ > 5)') возвращает 0 записей

Кроме того, были оптимизированы и некоторые функции, которые уже работали с JSON раньше. Этим успешно занимался Никита Глухов.

Например, оператор #>>, соответствующий функциям jsonb_each_text() и jsonb_array_elements_text(), раньше достаточно быстро преобразовывал JsonbValue в text, но работал неторопливо с другими типами. Сейчас всё работает быстро.

2. Поддержка быстрого поиска ближайших соседей в индексах SP-GiST (KNN)

(Add support for nearest-neighbor (KNN) searches of SP-GiST indexes. Nikita Glukhov, Alexander Korotkov, Vlad Sterzhanov)

Никита Глухов и Александр Коротков из нашей компании продолжили работу, начатую Владом Стержановым из Минска (он же Quadrocube). Postgres был первой СУБД, которая реализовала поиск ближайших соседей — раньше Oracle и MS, причём гораздо более прямым и удобным способом — и это заслуга Олега Бартунова и его команды. Идея этого поиска в оригинальном алгоритме обхода дерева, дающем в большинстве случаев огромный выигрыш. Поиск ближайших соседей используется много где, но в ГИС особенно часто.

Влад сделал патч KNN-поиска для для работы с пространственными индексами SP-GiST для quad-деревьев, когда плоскость делят на квадраты фиксированного размера, и для KD-деревьев, то есть k-мерных деревьев.

Александр Коротков, ментор Влада по GSoC (Google Summer of Code), продолжил разработку с коллегой из Postgres Professioanl Никитой Глуховым. Была серьезно обогащена функциональность: улучшено внутренние кэширование данных при обходе дерева, добавлены классы операторов для окружностей и многоугольников с упорядочением по расстоянию.

Чтобы воспользоваться алгоритмом поиска ближайших соседей, достаточно написать ORDER BY [выражение, содержащее оператор расстояния], и тогда оптимизатор автоматически подключит этот алгоритм. Например,


   SELECT * FROM polygons 
         ORDER BY poly <-> point '(0,0)';

Патчи Никиты Глухова можно увидеть на гитхабе.

3. Оптимизация блокировок для ускорения вставки в индексы B-Tree

Release Notes это Improve speed of btree index insertions by reducing locking overhead. Alexander Korotkov)

Александру Короткову, главному системному архитектору Postgres Professional, удалось придумать более разумный алгоритм блокировок при вставке в индексы B-tree. Выигрыш после применения этого патча заметен в случаях, когда вставка происходит более или менее «подряд». Измерения на 72-ядерном сервере показали, что в этом случае выигрыш доходит до 50%. При хаотичной вставке выигрыш не так заметен.

4. Экономный WAL

(Reduce the WAL write overhead of GiST, GIN, and SP-GiST index creation. Anastasia Lubennikova, Andrey V. Lepikhov)

Эта серия патчей позволяет сократить WAL-трафик, генерируемый при создании индексов GiST, GIN и SP-GiST. Теперь можно логировать страницы таких индексов только один раз — в конце, когда индекс уже построен. А в случае ошибки при построении индекса записи в WAL о неудачных попытках вообще не появятся. Раньше такое было возможно только при создании B-tree и RUM. Патчи используют механизм generic WAL.

Для проверки размера xlog приложены скрипты. Тестирование на базе данных IMDB (формат JSON), в которой 4М+ записей, занимающих 4ГБ, показало:

CREATE INDEX ON imdb USING gin(jb jsonb_path_ops);

старым способом исполнялся 205 секунд, WAL 3.2 ГБ, а новый алгоритм дал 133 секунды, и WAL 0.4 ГБ.

5. Оптимизация сканирования index-only в случае многих колонок.

(Allow index-only scans to be more efficient on indexes with many columns. Konstantin Knizhnik)

При анализе работы базы одного из клиентов нашей компании обнаружилось, что один и тот же запрос исполняется в некоторых случаях дольше на 25% с index only scan, чем с index scan (enable_indexonlyscan = off).
Это происходило, когда SELECT выполнялся по многим полям, которые имели в основном тип bytea, и их офсеты не кэшировались, так как у таких полей нет фиксированного смещения (об этом см. также доклад Николая Шаплова «Что у него внутри»). Чтобы распаковать k-й атрибут, надо распаковать предыдущие k-1. Распаковка записи по одному атрибуту требует O(N*N) времени, где N — число полей. Эти 25% случились уже при 10 полях.

Константин Книжник воспользовался алгоритмом, который используется при работе с хипом: при обращении к k-ому атрибуту, достаются и запоминаются предыдущие k-1, время растет линейно с числом полей. После применения патча время выполнения с index scan и index only scan практически не отличается.

6. Контроль сброса WAL-сегментов на диск

(Add a wait event for fsync of WAL segments. Konstantin Knizhnik)

Ядро PostgreSQL мониторит запись в WAL, но не следит за сбросом сегментов WAL из памяти на диск, то есть за fsync. К.Книжник сделал патч, который создает новый тип события, оно называется теперь WALSync (внутреннее имя переменной WAIT_EVENT_WAL_SYNC). Его можно увидеть в табличке PG-событий с пояснением «Ожидание сброса WAL-файла в надежное хранилище». Этот вопрос обсуждался в рассылке hackers.

Как долго происходит сброс, обычно неизвестно: стандартный PostgreSQL не умеет агрегировать такую статистику. Но есть расширение pg_wait_sampling, написанное в Postgres Professional. Оно умеет рассказывать о том, в ожидании каких событий Postgres проводит время. Теперь, когда событие добавлено, можно следить и за fsync.

7. Поддержка новых языков в stemmer-словарях

(Update Snowball stemmer dictionaries with support for new languages. Arthur Zakirov)

Раз конференции по Postgres проходят в Непале, куда уж естественней добавить непальский язык в базу! Это и было сделано. Благодаря усилиям Артура Закирова теперь можно пользоваться непальским стемминг-словарем на Snowball.

8. Функции to_timestamp()/to_date() стали толерантней к данным

(Adjust to_timestamp()/to_date() functions to be more forgiving of template mismatches, Artur Zakirov, Alexandehttps://postgrespro.ru/docs/postgresql/12/logfile-maintenancer Korotkov, Liudmila Mantrova)

Функция to_timestamp() не работала, если обрабатывалась строка формата с лишними пробелами. Обсуждение бага в to_timestamp() вылилось в пространную дискуссию о том, какое поведение функций to_timestamp() и — заодно — to_date() считать правильным. К всеобщей выгоде обе функции стали более терпимыми к лишним пробелам в строке формата и строке ввода.

9. Логи можно ротировать через pg_ctl

(Allow control of log file rotation via pg_ctl. Kyotaro Horiguchi, Alexander Kuzmenkov, Alexander Korotkov)

Другими словами утилита pg_ctl обзавелась новой опцией:

pg_ctl logrotate [-D каталог_данных] [-s]

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

10. Возможность создавать новые типы таблиц (Pluggable storage)

(Add CREATE ACCESS METHOD command to create new table types. Andres Freund, Haribabu Kommi, Álvaro Herrera, Alexander Korotkov, Dmitry Dolgov)

Этот важный патч — существенная часть инфраструктуры Pluggable Storage API, отсюда и международный состав разработчиков патча. Команда CREATE ACCESS METHOD работает в Postgres с версии 9.6. Но до 12-й можно было только создавать индексные методы доступа. Вот что в документации 11-й версии:

CREATE ACCESS METHOD имя
    TYPE тип_метода_доступа
    HANDLER функция_обработчик
< ... >
тип_метода_доступа
Это предложение задаёт тип создаваемого метода доступа. В настоящее время поддерживается только INDEX.

А в документации к 12-й уже читаем: в настоящее время поддерживается только TABLE и INDEX. Межу прочим, в 11-й команда CREATE ACCESS METHOD обеспечивалась расширением Postgres Pro, а в 12-й — уже PostgreSQL.

Исполнение операции зависит от типа метода доступа; если это тип TABLE, то обрабатывать будет table_am_handler, а если тип INDEX, то index_am_handler (раньше: для методов доступа типа INDEX это должен быть index_am_handler). Появилась целая глава в документации о табличных методах.

При создании таблицы теперь можно задавать ее тип:

CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXISTS ] имя_таблицы ( [
<  ...  >
[ USING метод ]

метод имеет тип TABLE — это и есть отсылка к Pluggable Storage. Теперь он heap по умолчанию, а раньше другого, собственно, и не было. О классах операторов здесь

default_table_access_method (string)

Этот параметр задаёт табличный метод доступа по умолчанию, который будет использоваться при создании таблиц или материализованных представлений, если в команде CREATE не будет явно указан метод доступа, или при выполнении команды SELECT… INTO, в которой явно задать метод доступа нельзя. Значение по умолчанию — heap. Большая дискуссия в hackers поможет разобраться в деталях.

До этого момента мы говорили о новшествах. Но ресурсы времени программистов отъедает и исправление багов. Главные из них:

11. Баг: ошибка в одной из структур

Extra quote_all_identifiers in _dumpOptions. Arthur Zakirov)

В общем, ничего особенного, найдена ошибка в одной из структур, которые использует pg_dump — ее пропустил компилятор. Но сам Брюс Момджян похвалил за находку.

О других проблемах с DumpOptions можно почитать здесь.

12. Баг в реприликации:

(xlogreader: do not read a file block twice. Arthur Zakirov)

Другой сотрудник нашей компании, разработчик pg_probackup Григорий Смолкин обнаружил, что одна из наших утилит тормозится, когда xlogreader читает zlib-архивы. Оказалось, что иногда он читает файловые блоки WAL дважды.

Если архивы читаются непоследовательно, то производительность оставляет желать лучшего. Повторное чтение блока всегда непоследовательно, так как приходится возвращаться к пройденной позиции вызовом функции gzseek(). Теперь лишнего перечитывания не происходит.

P.S. Не буду лукавить: дюжина патчей (строго говоря дюжина серий патчей) не просто случайное совпадение с номером версии Postgres. Список вполне мог бы быть недо-дюжиной или сверх-дюжиной. Мне подумалось, что так будет красивей, а красота отчасти и двигатель программирования, не говоря о других областях человеческой деятельности.

Автор: Igor_Le

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js