PostgreSQL и .NET

PostgreSQL редко тормозит "просто потому что PostgreSQL"

Чаще проблема начинается выше.

В LINQ. В лишнем Include. В ToList() не там. В выборке всей сущности, когда нужен один столбец. В фильтре, под который нет нормального индекса. В запросе, который красиво выглядит в C#, но превращается в SQL с болью.

Я видел это много раз: команда сначала пытается "ускорить базу", а потом оказывается, что база честно делает то, что ей сказали.

Просто сказали не очень удачно.

Если я смотрю на медленный запрос в .NET + PostgreSQL, я обычно не начинаю с "давайте добавим индекс".

Я начинаю с другого:

1. Смотрю реальный SQL, который сгенерировал EF Core. 2. Прогоняю EXPLAIN ANALYZE. 3. Проверяю, сколько строк реально читается. 4. Смотрю, есть ли seq scan там, где его не ждали. 5. Проверяю, не тащим ли мы лишние колонки. 6. Убираю tracking там, где это read-only запрос. 7. Проверяю N+1 и лишние roundtrip-ы. 8. И только потом думаю про индекс.

Индексы тоже бывают разные.

Иногда нужен обычный btree.

Иногда partial index, если запрос почти всегда ходит по активным/неудалённым/опубликованным записям.

Иногда covering index через INCLUDE, чтобы PostgreSQL мог закрыть запрос из индекса и не лезть в таблицу.

А иногда индекс вообще не спасает, потому что запрос написан так, что planner не может нормально им воспользоваться.

В EF Core это особенно легко пропустить.

Написал красиво:

csharp var users = await db.Users .Where(x => x.IsActive) .ToListAsync();

А потом где-то выше оказалось, что тебе нужны только Id и Name.

И база таскает больше данных, чем нужно. Сеть гоняет больше данных, чем нужно. EF трекает больше объектов, чем нужно. GC потом тоже передаёт привет.

Более скучный вариант часто быстрее:

csharp var users = await db.Users .AsNoTracking() .Where(x => x.IsActive) .Select(x => new { x.Id, x.Name }) .ToListAsync();

Это не "микрооптимизация".

На маленькой таблице разницы почти нет. На живой системе под нагрузкой такие мелочи внезапно становятся причиной лишней latency, лишней памяти и лишней нагрузки на базу.

Мой вывод простой: оптимизация PostgreSQL в .NET начинается не с магического индекса.

Она начинается с вопроса:

"Какой SQL мы реально отправили в базу?"

А уже потом:

"Почему PostgreSQL выбрал именно такой план?"

Если этого шага нет, можно долго лечить базу от запроса, который приложение вообще не должно было генерировать.

#dotnet #postgresql #backend #performance