Ответ
Отладка медленного запроса — это системный процесс. Вот мой подход, основанный на практике с C# и SQL Server:
-
Сбор данных:
- Включаю логирование запросов в EF Core для захвата сгенерированного SQL.
optionsBuilder.LogTo(Console.WriteLine, LogLevel.Information); - Использую SQL Server Profiler, Extended Events или запросы к динамическим административным представлениям (DMV), например
sys.dm_exec_query_stats, чтобы найти самые затратные запросы.
- Включаю логирование запросов в EF Core для захвата сгенерированного SQL.
-
Анализ плана выполнения: Получаю и анализирую Actual Execution Plan в SSMS (Ctrl+M). Ищу:
- Table/Index Scans вместо Index Seeks.
- Key Lookups (RID Lookups) — часто указывают на недостающий покрывающий индекс.
- Высокую стоимость операций сортировки (Sort), хэширования (Hash Match) или удаленного сбора данных (Remote Query).
- Предупреждения (жёлтые значки) о неверных оценках количества строк или отсутствующих индексах.
-
Оптимизация: Действия зависят от анализа:
- Добавление/изменение индексов: Чаще всего проблема здесь. Создаю недостающие индексы, покрывающие условия WHERE, JOIN и SELECT.
CREATE INDEX IX_Orders_CustomerId_Status ON Orders(CustomerId, Status) INCLUDE (TotalAmount); - Рефакторинг запроса: Переписываю сложный запрос, разбиваю на части, устраняю N+1 проблему в EF Core с помощью
Include()иSelect(). - Использование возможностей ORM: Применяю
AsNoTracking()для операций только для чтения,AsSplitQuery()для избежания избыточных данных в некоторых сценариях. - Кэширование: Для редко меняющихся данных рассматриваю кэширование результатов запроса (например, с помощью
IMemoryCacheилиIDistributedCache).
- Добавление/изменение индексов: Чаще всего проблема здесь. Создаю недостающие индексы, покрывающие условия WHERE, JOIN и SELECT.
-
Тестирование: Замеряю производительность до и после изменений с помощью
Stopwatchили сравниваю метрики в плане выполнения (логические чтения, затраченное время).