Имеет ли смысл создавать индекс по колонке с 90% одинаковых значений в базе данных
Что проверяют
Понимаете ли вы, что польза индекса зависит не от колонки в целом, а от того, какое значение ищет запрос. Правильный ответ «зависит от запросов», и интервьюер ждёт, что вы раскроете это «зависит». Следом обычно просят показать, как проверить решение, и спрашивают про частичные индексы.
Как отвечать
Разберите два случая:
- ищут частое значение: индекс бесполезен. Планировщик прочитает таблицу последовательно, потому что выбрать 90% строк через индекс дороже из-за случайных чтений страниц;
- ищут редкое значение: индекс очень полезен, особенно если значение встречается в долях процента. Типичный пример:
status, где почти всёdone, а фоновый воркер постоянно выбираетpending.
Планировщик PostgreSQL про перекос знает: ANALYZE собирает в pg_stats частые значения и их доли (most_common_vals, most_common_freqs), поэтому для done выберет Seq Scan, а для pending индекс. В MySQL 8.0 для этого есть гистограммы.
Лучшее решение здесь частичный индекс только по редким строкам: CREATE INDEX ON tasks (created_at) WHERE status = 'pending'. Он в разы меньше, дешевле обновляется и сразу отдаёт нужный порядок. В Django это models.Index(fields=["created_at"], condition=Q(status="pending"), name="...") в Meta.indexes, в SQLAlchemy параметр postgresql_where у Index. Другой вариант: составной индекс, где колонка идёт в паре с более селективной.
Проверяют решение через EXPLAIN (ANALYZE, BUFFERS) на реальном объёме данных, а неиспользуемые индексы видно в pg_stat_user_indexes по нулевому idx_scan.
Где спотыкаются
Отвечают «нет, низкая кардинальность, индекс не нужен» и не спрашивают, какое значение ищут. Ровно этот ответ вопрос и ловит.
Не учитывают цену записи: полный индекс по такой колонке обновляется на каждой вставке и смене статуса, хотя 90% его записей никто не читает.
Проверяют план на dev-базе с тысячей строк. На маленькой таблице планировщик почти всегда выберет Seq Scan, и вывод о бесполезности индекса окажется ложным.