Когда речь заходит об оптимизации запросов в PostgreSQL, разработчики, как правило, сосредотачиваются на времени выполнения: индексы, планы запросов, настройки памяти и так далее. Время планирования остаётся в тени. А зря! Планировщик работает перед каждым выполнением запроса. Для OLTP-нагрузки с короткими транзакциями накладные расходы на планирование могут составлять значительную долю от общего времени ответа. Для запросов с большими IN-списками и высоким statistics_target планирование может занимать сотни миллисекунд, тогда как само выполнение укладывается в миллисекунды. Именно поэтому ускорение планировщика не менее важно, чем ускорение выполнения. Эта статья - о том, как мы нашли и устранили одну из таких скрытых проблем.
Чтобы понять суть проблемы, нужно разобраться, как PostgreSQL оценивает селективность запросов. Когда планировщик строит план, ему нужно оценить, сколько строк вернёт тот или иной предикат. Для этого он опирается на статистику, которую собирает ANALYZE. Одна из ключевых структур в этой статистике - список наиболее часто встречающихся значений, MCV (Most Common Values). Для каждой колонки PostgreSQL хранит до statistics_target таких значений вместе с их частотами. По умолчанию statistics_target равен 100, но его можно увеличить - глобально через default_statistics_target или для конкретной колонки через ALTER TABLE:
statistics_target
ALTER TABLE users ALTER COLUMN status SET STATISTICS 1000;