Какие аргументы против установки индекса на все колонки таблицы
Что проверяют
Понимаете ли вы цену индекса и умеете ли решать, какие индексы нужны, исходя из запросов ORM, а не из списка полей модели. Следом часто спрашивают, как вы увидите SQL, который на самом деле генерирует Django или SQLAlchemy.
Как отвечать
Основные аргументы:
- медленнее запись: каждый индекс обновляется при вставке и удалении, а в PostgreSQL изменение индексированной колонки отключает HOT-оптимизацию, и новая версия строки попадает во все индексы. Особенно заметно на
bulk_createи ETL-загрузках, перед которыми индексы иногда снимают; - место: индексы занимают диск и память и вытесняют из кеша горячие данные;
- многие бесполезны: одиночные индексы не заменяют составной под конкретный фильтр с сортировкой, а на колонке с двумя или тремя значениями индекс вообще не используется;
- миграции: каждый новый индекс это миграция, которая на большой таблице без
CONCURRENTLYблокирует запись.
Как это выглядит в Python-стеке. В Django индекс задают через db_index=True или Meta.indexes с models.Index(fields=[...]), там же составные и частичные индексы с condition. На больших таблицах в PostgreSQL используют AddIndexConcurrently из django.contrib.postgres.operations, а миграцию помечают atomic = False. В Alembic то же делает op.create_index(..., postgresql_concurrently=True) внутри autocommit_block(). Нужные индексы находят через QuerySet.explain(), django-debug-toolbar или pg_stat_statements, лишние через pg_stat_user_indexes.
Где спотыкаются
Добавляют в Meta.indexes индекс по полю ForeignKey, не зная, что Django уже создаёт индекс на внешнем ключе. Получается дубль. А если есть составной индекс, который начинается с этого поля, одиночный индекс FK становится лишним, и его отключают через db_index=False.
Не знают, что для CharField с db_index=True Django в PostgreSQL создаёт второй индекс с varchar_pattern_ops под запросы LIKE. Индексов в базе больше, чем видно по модели.
Оценивают пользу индекса на тестовой базе из сотни строк. Там планировщик выберет последовательное чтение, так что выводы делают на объёме, близком к продовому, с EXPLAIN ANALYZE.