Skip to content

Latest commit

 

History

History
217 lines (150 loc) · 9.23 KB

File metadata and controls

217 lines (150 loc) · 9.23 KB
layout default
title 🔍 Как диагностировать и оптимизировать запросы к БД?
description
author Dvurechensky
date 2025-08-28
published true
tags
sql
trace
C#

🔍 Как диагностировать и оптимизировать запросы к БД?

Typing SVG

Static Badge

✨ Оглавление

⬆ Вернуться к главной


1️⃣ Почему диагностика и оптимизация важны

Даже идеальный код на C# не спасёт, если SQL работает медленно:

  • Высокая нагрузка на сервер БД → блокировки, таймауты.
  • Плохое использование индексов → full table scan.
  • Много лишних запросов → N+1 проблема.
  • Потеря производительности приложения → UI тормозит, Web API задержки.

2️⃣ Этапы диагностики

a) Логирование и профилирование запросов

  • EF Core:
var sql = context.Users.Where(u => u.IsActive).ToQueryString();
Console.WriteLine(sql);
  • SQL Server Profiler / Extended Events / Query Store – отслеживание фактических SQL-запросов, времени выполнения, планов.
  • MiniProfiler / EFCore Diagnostics – лёгкие инструменты для приложений.
  • Каверзный момент: EF может генерировать сложные JOIN/подзапросы без очевидной причины → всегда проверяй сгенерированный SQL.

b) Проверка планов выполнения (Execution Plan)

  • В SQL Server: SET STATISTICS PROFILE ON; или просмотр Execution Plan.

  • Что искать:

    • Full Table Scan vs Index Seek.
    • Nested Loops vs Hash Join.
    • Сортировки и вычисления на сервере.
  • Каверзный момент: EF иногда приводит к созданию неиспользуемых индексов или сложных подзапросов.


c) Анализ индексов

  • Правильные индексы ускоряют SELECT, но замедляют INSERT/UPDATE/DELETE.
  • Используй INCLUDE колонки для covering index.
  • Каверзный момент: лишние индексы → нагрузка на запись, мало индексов → медленный SELECT.

d) Замеры времени

  • В .NET: Stopwatch для измерения времени запроса.
var sw = Stopwatch.StartNew();
var data = await context.Users.AsNoTracking().ToListAsync();
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
  • Каверзный момент: измерение без AsNoTracking может показывать дополнительное время на трекинг объектов.

e) Проверка объёма данных

  • Измеряй, сколько реально строк возвращается.
  • Используй Count(), Take(10) для проверки, чтобы не тянуть миллионы строк в память.
  • Каверзный момент: ToList() без фильтрации → memory spike.

3️⃣ Методы оптимизации запросов

a) Фильтрация на сервере

  • Никогда не фильтруй после ToList() в памяти.
// Плохо
var data = context.Users.ToList().Where(u => u.IsActive);
// Хорошо
var data = context.Users.Where(u => u.IsActive).ToList();

b) Проекции

  • Выбираем только нужные колонки.
var data = context.Users
                  .Where(u => u.IsActive)
                  .Select(u => new { u.Id, u.Name })
                  .ToList();
  • Каверзный момент: иногда Include + Select работает медленнее, чем несколько отдельных запросов.

c) Отложенное выполнение и асинхронность

  • LINQ запросы выполняются только при materialization (ToList, First, Count).
  • Используй async для Web и UI потоков.
var users = await context.Users.Where(u => u.IsActive).ToListAsync();

d) Batch/пакетная обработка

  • Для массовых операций использовать BulkInsert/BulkUpdate или Dapper.
  • EF Core по умолчанию делает отдельные INSERT/UPDATE → медленно.
  • Каверзный момент: при больших вставках EF может генерировать миллионы команд → OutOfMemory.

e) Использование индексов и оптимизация JOIN

  • Проверяй, что JOIN-ы используют ключи.
  • Сортировка и фильтры по индексированным колонкам → быстрее.
  • Каверзный момент: фильтры на вычисляемых колонках могут игнорировать индексы.

f) Пагинация

  • Используй Skip/Take с OrderBy.
var page = context.Users
                  .OrderBy(u => u.Id)
                  .Skip((page-1)*pageSize)
                  .Take(pageSize)
                  .ToList();
  • Без OrderBy → непредсказуемый результат.

g) Кэширование

  • Кэш на уровне приложения: MemoryCache, Redis.
  • Кэш на уровне EF: ChangeTracker + AsNoTracking.
  • Каверзный момент: кэш устарел → возвращает старые данные.

h) Минимизировать количество соединений

  • Используй один DbContext на единицу работы.
  • Не держи DbContext долго → рост памяти и трекинга.

i) Использование инструментов профилирования

  • SQL Profiler / Query Store – для SQL Server.
  • EXPLAIN / EXPLAIN ANALYZE – для PostgreSQL.
  • MiniProfiler / Glimpse – в приложении.

⬆ Вернуться к главной

✨Dvurechensky✨