Postgres на пределе: как мы ускорили запросы в 40 раз
Всё началось с алерта: p95 latency дашборда вырос с 300 мс до 4 секунд. Виновник — «простой» отчётный запрос, который кто-то добавил полгода назад и все успешно забывали про него до конца квартала.
Первое правило оптимизации: не гадай, а смотри план. EXPLAIN ANALYZE показал sequential scan по 40 млн строк и функцию lower(email) в условии, из-за которой обычный индекс молча не использовался.
-- было: индекс не работает из-за функции в WHERE WHERE lower(email) = $1 -- стало: expression-индекс CREATE INDEX idx_users_email_lower ON users (lower(email));
Дальше — частичный индекс для горячих строк (status = 'active'), covering-индекс с INCLUDE и перенос агрегации в материализованное представление, обновляемое триггером.
Итог: 4.1 с → 98 мс на холодном кэше. Но главный вывод другой: без культуры чтения планов запросов любая «мелочь» в WHERE однажды станет ночным инцидентом.
Индекс, который не используется, — это просто дорогое место на диске. Проверяйте планы после каждого релиза схемы.
Комментарии 2
Классика с lower(email) в WHERE 😄 Добавлю: ещё проверяйте pg_stat_user_indexes, там часто лежат мёртвые индексы.
40 раз — это красиво. У нас после такого же фикса p95 упал с 6с до 200мс, тимлид не поверил без скриншотов.