EF сортировка и поиск по страницам — медленно, как заказано дважды?

#c# #entity-framework #entity-framework-6

#c# #entity-framework #entity-framework-6

Вопрос:

У меня есть простая сущность, и у меня их 100 000 в моей базе данных SQL Sever 2012:

 public class Entity
{
    public int Id { get; set; }
    public string Field1 { get; set; }
    public string Field2 { get; set; }
}
  

Я хочу показать их в виде сетки, разбитой на страницы, поскольку 100 000 — это слишком много для отображения на одном экране (и не очень эффективно). Сетка должна разрешать сортировку и фильтрацию — очевидно, что все 3 из этих операций лучше всего выполнять на сервере, и EF должен их переводить.

Итак, давайте получим вторую страницу из 500, отсортированную по полю 1:

 var items = context.Entities.OrderBy(e => e.Field1).Skip(500).Take(500);
  

Когда этот запрос выполняется, он занимает 12 секунд! Итак, я покопался в нем и обнаружил, что он переведен следующим образом:

 SELECT TOP (500) [Extent1].[Id]     AS [Id],
                 [Extent1].[Field1] AS [Field1],
                 [Extent1].[Field2] AS [Field2]               
FROM   (SELECT [Extent1].[Id]     AS [Id],
               [Extent1].[Field1] AS [Field1],
               [Extent1].[Field2] AS [Field2],
               row_number() OVER (ORDER BY [Extent1].[Field1] ASC) AS [row_number]
        FROM   [dbo].[Costs] AS [Extent1]) AS [Extent1]
WHERE  [Extent1].[row_number] > 500
ORDER  BY [Extent1].[Field1] ASC        
  

Конечно, это сортируется дважды? Я не эксперт по SQL, но подзапрос упорядочивает по полю1 и присваивает этот порядок row_number . Затем мы берем ВЕРХНИЕ 500 row_numbers за 500, чтобы получить до 500 строк для страницы 2. Нам не нужно снова упорядочивать результаты по полю1.

Если я удалю финал ORDER BY [Extent1].[Field1] ASC , результаты запроса будут одинаковыми, а время выполнения сократится примерно до 150 миллисекунд.

Итак, очевидно, что 150 мс предпочтительнее 12 с — есть ли что-то, что я делаю неправильно? Могу ли я что-нибудь сделать, чтобы это исправить?

Обновить

План запроса одинаков для обоих. Единственное различие во всплывающей подсказке для сортировки — это «Фактическое количество строк», равное 4604 для запроса 12s и 1134 для запроса 150ms. Я бы добавил, что это сгенерированные данные из фиксированного списка из 15 слов (для этого теста) — т.е. Поле 1 содержит 1 из 15 значений, так что по существу существует 15 групп по 6666 строк.

План запроса

(щелкните для увеличения изображения)

Резервное копирование SQL Server 2012

Комментарии:

1. Если бы это не было заказано во второй раз, SQL Server мог бы свободно возвращать вам записи с помощью row_number 501, 600, 502, 601, 503… даже 12 300 («дайте мне 500 строк, число которых row_number больше 500″), и вы запросили их как OrderBy(e => e.Field1) .

2. То, что вы опубликовали, является типичным запросом подкачки. Любые проблемы, с которыми вы сталкиваетесь, почти наверняка связаны с отсутствием индексации. Запрос ничего не значит без плана его выполнения. ORDER BY Строка не означает, что сервер будет выполнять сортировку каждый раз, когда он с ней сталкивается. Сервер проверит наличие существующих индексов и будет использовать их для вычисления рейтинга и выбора строк для возврата. Я ожидаю, что у вас нет правильных индексов в поле подкачки (т.Е. Field1), что заставляет сервер выполнять полное сканирование и упорядочивание таблицы, прежде чем выбирать необходимые строки.

3. @ta.speot.is — Я понимаю, о чем ты говоришь. Подзапрос задает row_number в качестве индекса строки, упорядоченной по Field1 . Эмпирически этот запрос возвращается в порядке строк — это просто совпадение, разве это не гарантировано?

4. Я удивлен, что удаление окончательного заказа ПО результатам приводит к увеличению производительности. SQL Server сведет два порядка к одному физическому виду. Даже если бы сортировка выполнялась дважды, почему запрос стал бы в 100 раз медленнее, а не в 2 раза. Это не имеет смысла. Опубликуйте оба плана выполнения в виде изображений.

5. @CharlesMager: если у вас нет предложения order by, тогда нет необходимости заказывать что-либо. Часто бывает так, что вы хотите (как в этом случае), потому что SQL просто сортирует данные, и он не будет прилагать усилий для их сортировки. Иногда, хотя отсутствие order by будет иметь значение. Все зависит от того, как БД хранит данные внутри. И для записи я тоже удивлен, что удаление этой окончательной сортировки является такой проблемой, но не понимаю, почему это может быть.

Ответ №1:

Это связано с ошибкой / особенностью в SQL Server при объединении потоков TOP и Gather. Индекс исправит это, как и отключение параллелизма (глобально, или для этого пользователя, или для запроса). Подсказка заключалась в том, что потоки Gather передавались в базу данных tempdb, что является чрезвычайно редким состоянием. http://web.archive.org/web/20180220120719/http://sqlblog.com:80/blogs/paul_white/archive/2012/05/03/parallel-row-goals-gone-rogue.aspx Это материал 500-го уровня.

Обратите внимание, что вы не можете пропустить финал ORDER BY , потому что это делает порядок результатов неопределенным.

Ответ №2:

Я думаю, что ваша проблема связана с выполнением порядка в большом и неиндексированном столбце.

Пожалуйста, убедитесь, что вы выполняете индексацию для этого столбца.

В любом случае order by предложение было бы лучше со nvarchar значениями not