Какие проблемы возникают при использовании некластерного индекса и как их решить
Что проверяют
Понимаете ли вы, как некластерный индекс устроен в SQL Server и почему оптимизатор его иногда не берёт. Главное слово, которое ждут, это key lookup. Следом обычно спрашивают про покрывающий индекс и про то, как найти лишние индексы.
Как отвечать
Некластерный индекс хранит свой ключ и ссылку на строку: ключ кластерного индекса или RID, если таблица это куча. Отсюда проблемы:
- key lookup: для каждой найденной строки движок идёт за остальными колонками в кластерный индекс. На десятке строк это дёшево, на десятках тысяч оптимизатор предпочтёт полный скан. Решение: покрывающий индекс с
INCLUDE, в EF Core этоHasIndex(x => x.CustomerId).IncludeProperties(x => x.Total); - широкий кластерный ключ: он копируется в каждый некластерный индекс, поэтому
uniqueidentifierв роли кластерного ключа раздувает их все, а случайные GUID ещё и дробят страницы; - несаргабельные условия:
WHERE YEAR(CreatedAt) = 2026илиLIKE '%abc'индекс для поиска не используют. Первое переписывается в диапазон дат; - неявное преобразование: колонка
varchar, а параметр пришёл какnvarchar, в плане появляетсяCONVERT_IMPLICITи скан. Частая беда из C#: Dapper передаётstringкакnvarchar, нуженDbString { IsAnsi = true }, а в EF Core колонку надо честно описать черезIsUnicode(false); - цена записи: каждый индекс обновляется при
INSERT,UPDATEиDELETE, плюс место и лишние блокировки.
Лишние индексы ищут через sys.dm_db_index_usage_stats: много user_updates при нуле user_seeks. Для узких выборок пригодится фильтрованный индекс, в EF Core это HasFilter.
Где спотыкаются
Создают всё, что советуют DMV отсутствующих индексов или подсказка в плане. Существующие индексы там не учитываются, и так появляются дубли с разным порядком колонок.
Лечат медленные запросы регулярным ребилдом по старым порогам фрагментации. На SSD фрагментация влияет слабее, а ребилд заодно обновляет статистику, и ускорение на самом деле даёт она.
Путают INCLUDE и ключевые колонки: включённые колонки не участвуют в поиске и сортировке по индексу, они только избавляют от lookup.