Частичный индекс для редких значений
История о компактном partial index для редкого активного статуса в таблице с сотнями миллионов завершённых операций.
PostgreSQLИндексыПроизводительностьЛена, ведущий аналитик, уже час смотрела в монитор, как в бездну.
— Макс, это парадокс, — сказала она, когда Макс подошёл с неизменной кружкой кофе. — Я построила индекс, чтобы ускорить поиск. А он замедлил всё. Вставку. Обновление. И даже сам поиск.
Макс молча взглянул на её экран. Таблица operations, 500 миллионов строк. Индекс по полю status, занимающий 25 гигабайт.
— Какой статус ты ищешь? — спросил он, хотя уже знал ответ.
— active, — выдохнула Лена. — Но их там доли процента. Остальные 99.9% — это completed.
Макс кивнул. Он сделал глоток кофе и посмотрел на Лену. Не как на коллегу, а как на соучастника фундаментального заблуждения.
— Ты ищешь иголку в стоге сена. Тогда зачем ты индексируешь сено?
Лена моргнула. Вопрос был настолько простым, что казался абсурдным.
— Но… индекс работает по всей колонке. Так устроены базы данных.
— Так устроен мир по умолчанию, — поправил Макс. — Мир, который пытается быть справедливым ко всем и в итоге неэффективен ни для кого. Твой индекс — это огромная, подробная карта города, где отмечен каждый дом. Но ты каждый день ходишь только в одно здание. Зачем тебе карта всего города? Тебе нужен прямой, короткий путь к единственной нужной двери.
Он придвинул её клавиатуру. Пальцы легко пробежали по клавишам.
— Мы не будем составлять карту для всех. Мы создадим её только для избранных.
CREATE INDEX idx_operations_only_activeON operations (created_at DESC)WHERE status = 'active';— Этот индекс, — Макс показал на экран, — содержит только активные строки и сразу поддерживает сортировку дашборда по времени. Хранить status как ключ бессмысленно: внутри partial index он всегда равен одной и той же константе. Если дашборд ищет активную операцию по другому полю, именно это поле и нужно поставить в ключ.
— И ещё одно условие: запрос должен содержать предикат, из которого планировщик может вывести status = 'active'. Параметризованное или логически иначе записанное условие не всегда совпадёт с предикатом индекса на этапе планирования.
Он нажал Enter. После того как команда выполнилась Макс бросил взгляд на статистику:
-
Старый индекс: 25 Гб 🔴
-
Новый индекс: 100 Мб 🟢
— Обнови свой дашборд, — тихо сказал Макс.
Лена нажала F5. Графики, которые раньше вырисовывались несколько секунд, появились раньше, чем она успела убрать палец с клавиши.
Она молчала.
Макс допил кофе и улыбнулся.
— Иногда лучшее решение — не общее, а специализированное. Перестань строить дороги для всех. Проложи одну идеальную тропу туда, куда действительно часто ходишь.