Дополняем 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, аэропорт "Шереметьево"