Как выявлять проблемы с сортировкой и нехваткой памяти в базе данных
Что проверяют
Умеете ли вы доказать проблему по плану выполнения и системным представлениям, а не угадывать по симптому «запрос тормозит». Для .NET-разработчика важно ещё и понимать, где идёт сортировка: в базе или уже в памяти приложения после ToList(). Следом спрашивают, как исправить это без добавления памяти серверу.
Как отвечать
Сначала поймайте запрос: логирование SQL в EF Core через LogTo или ToQueryString(), тяжёлые запросы в Query Store у SQL Server или в pg_stat_statements у PostgreSQL. Дальше смотрите фактический план:
- SQL Server: оператор
Sortс предупреждением о сбросе вtempdbзначит, что выделенной памяти не хватило. ВMemoryGrantInfoплана видно, сколько памяти запрошено и сколько использовано. Сбросы ловятся и через Extended Events, событиеsort_warning; - очередь за памятью: ожидающие гранты видны в
sys.dm_exec_query_memory_grants, а в статистике ожиданий растётRESOURCE_SEMAPHORE; - PostgreSQL:
EXPLAIN (ANALYZE, BUFFERS)покажетSort Method: external mergeс объёмом на диске вместоquicksort. Порог задаётwork_mem, по умолчанию 4 МБ на каждую операцию сортировки, аlog_temp_filesпишет в лог временные файлы.
Причин обычно три: нет индекса, отдающего строки в нужном порядке; устаревшая статистика, из-за которой оптимизатор ошибается в числе строк и просит слишком мало памяти; сортируются широкие строки с лишними колонками. Лечится индексом по колонкам ORDER BY, обновлением статистики, выборкой только нужных полей и пагинацией по ключу вместо глубокого OFFSET.
Где спотыкаются
Смотрят оценочный план вместо фактического. Предупреждение о сбросе и реальное число строк есть только в фактическом.
Предлагают сразу поднять память сервера или work_mem глобально. В PostgreSQL лимит действует на каждую операцию, и сотня параллельных запросов съест всю память.
Не замечают, что сортирует сам код: ToList() до OrderBy грузит всю таблицу в процесс, и проблема памяти оказывается в приложении, а не в базе.