Search
Write a publication
Pull to refresh
30
10.4

PostgreSQL Developer / Database Server

Send message

Дополняем EXPLAIN Postgres'a информацией об использованной статистике.

Незадолго до код-фриза PostgreSQL 18, Роберт Хаас закомитил возможность, разрешающую внешним модулям добавлять в EXPLAIN дополнительную информацию.

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

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

В список опций EXPLAIN был добавлен параметр STAT, принимающий булевы значения ON/OFF. Если он включён, то в конец эксплейна будет вставляться информация об использованной статистике: наличии MCV, гистограммы, количестве элементов в них. А также значения stadistinct, stanullfrac и stawidth.

Зачем это нужно? - спросите вы. Ведь набор статистик прямо следует из списка выражений, участвующих в запросе? Разве нельзя понять, какая статистика была непосредственно использована, заглянув в код cost-model того или иного вида выражения?

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

CREATE TABLE sc_a(x integer, y text);
INSERT INTO sc_a(x,y) (
SELECT gs, 'abc' || gs%10 FROM generate_series(1,100) AS gs);
VACUUM ANALYZE sc_a;
LOAD 'pg_index_stats';

EXPLAIN (COSTS OFF, STAT ON)
SELECT * FROM sc_a s1 JOIN sc_a s2 ON true
WHERE s1.x=1 AND s2.y LIKE 'a';

Nested Loop
-> Seq Scan on sc_a s1
Filter: (x = 1)
-> Seq Scan on sc_a s2
Filter: (y ~~ 'a'::text)
Statistics:
"s2.y: 1 times, stats: {

MCV: 10 values, Correlation,
ndistinct: 10.0000, nullfrac: 0.0000, width: 5 }
"s1.x: 1 times, stats: {

Histogram: 0 values, Correlation,
ndistinct: -1.0000, nullfrac: 0.0000, width: 4 }

Здесь можно увидеть, что была использована статистика по колонкам s1.x и s2.y.
При этом, у нас всего десять MCV значений по y, а по х MCV статистика отсутствует вовсе; гистограмма вроде есть, но нулевой длины. И никаких нуллов в обеих колонках.

Таким образом, мы имеем некоторую полезную информацию, которая может подсказать логику выбора плана оптимизатором. Учитывая, что клиент, который не может предоставить данные, крайне редко может предоставить дамп таблицы pg_statistic, то такая достаточно безобидная информация может оказаться полезным подспорьем и вскрыть возможные причины проблем с выбором плана запроса.

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

А какие вы видите варианты применения возможностей расширения вывода эксплейна? Насколько в действительности безобидна даже такая ограниченная информация?

THE END.
12 апреля 2025, аэропорт "Шереметьево"

Tags:
Total votes 2: ↑2 and ↓0+2
Comments0

Module Info в бинарных файлах модулей Postgres

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

Каждый модуль, который вы попытаетесь загрузить в Postgres, в обязательном порядке содержит в своём теле информацию о версии ядра, для которого оно было собрано и параметрах его сборки. Обоснование необходимости этого можно найти в треде с обсуждением этой фичи, однако идея прозрачна: это сделано для того, чтобы предотвратить ошибку загрузки несовместимого модуля и связанные с этим нестабильности в работе инстанса СУБД.

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

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

Гораздо проще было бы, если бы ядро содержало в себе фунцию, например, module_info(module_name), которая позволяла бы изнутри СУБД (например, в консоли psql) определить полный путь и имя файла, содержащего искомый модуль. Более того, при наличии двух версий одного модуля в ядре (да, бывает и такое!), мы получаем возможность обнаруживать потенциальные конфликты. При этом, появляется возможность автоматизировать обнаружение модулей и их версий в системе другими модулями - да, я ненавижу использовать функцию SerializeLibraryState и другие грязные хаки для этой цели!

Ещё одна причина (уже глубоко техническая) связана с тем, что теперь (с апреля 2024 г.) расширения для текущей и последующих версий Postgres могут использовать dynamic shared memory (DSM) без необходимости быть загружаемыми при старте инстанса. Это открыло путь разработки легковесных модулей, которые могут быть загружены динамически в каждом отдельном бэкенде. Помимо производительности преимущество здесь в том, что появляется возможность реализовать технику онлайн-апгрейда расширения - установив новую версию модуля в системе под другим именем и загружая такую новую версию во вновь стартующих бэкендах мы имеем обновление функциональности на лету, без остановки инстанса! - по крайней мере, у меня в голове вырисовывается именно такой сценарий.

С другой стороны, проект omnigres (автор Yurii Rashkovskii) также пришёл к идее версионирования модулей, хотя и делает это внешним, по отношению к ядру, путём.

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

Перед тем, как начинать долгий путь обсуждения кода в hackers mailing list будет очень полезно аккумулировать опыт и мнения других разработчиков и мэйнтейнеров расширений Postgres. Нужна ли такая фича? Должна ли она быть опциональной или обязательной? Какая информация о модуле нужна (или просто будет полезна) в ваших инсталляциях?

Предлагайте ваши идеи и делитесь своим мнением в комментах, или в github-дискуссии сообщества PGEDC разработчиков расширений Postgres. Каждое мнение имеет ценность!

Tags:
Total votes 1: ↑1 and ↓0+1
Comments0

Information

Rating
964-th
Location
Madrid, Madrid, Испания
Registered
Activity

Specialization

Backend Developer
Lead
Database
PostgreSQL
Linux
Bash
SQL
Git