Entity Framerowk Skip/Take очень медленный, когда число, которое нужно пропустить, является большим

Итак, код очень прост:

var result = dbContext.Skip(x).Take(y).ToList();

Когда x большой (~ 1.000.000), запрос выполняется очень медленно. y мало - 10, 20.

SQL-код для этого: (из sql-профайлера)

SELECT ...
FROM ...
ORDER BY ...
OFFSET x ROWS FETCH NEXT y ROWS ONLY

Вопрос в том, кто-нибудь знает, как ускорить такой пейджинг? Спасибо.

Ответ 1

Я думаю, что OFFSET.. FETCH очень полезно при просмотре первых страниц из ваших больших данных (что происходит очень часто в большинстве приложений) и имеет недостаток производительности при запросе страниц высокого порядка из больших данных.

Прочтите статью для получения более подробной информации о производительности и альтернативах OFFSET.. FETCH.

Попробуйте применить к вашим данным столько фильтров, прежде чем применять пейджинг, чтобы пейджинг выполнялся с меньшим объемом данных. Трудно представить, что пользователь не хочет перемещаться по строкам 1М.

Ответ 2

Перемещение по миллиону записей в базе данных всегда будет медленным по сравнению с другими способами, база данных должна "пропустить" миллион записей, она делает это, создавая результат в памяти и затем отбрасывая первые миллионы строк.

Задумывались ли вы о альтернативе без sql (solr, lucene и т.д.), по крайней мере, чтобы сначала получить идентификаторы ваших строк, а затем использовать запрос id в()?

В качестве альтернативы вы можете иметь таблицу поиска (приготовленную таблицу) вашей основной таблицы с минимальными минимальными данными и идентификаторами, поэтому вы можете пропустить это и получить идентификаторы и запросить таблицу с ними.

Ответ 3

Вы правы на этом, Skip(). Метод Take() медленный на сервере SQL. Когда я заметил, что использовал другой подход, он работал хорошо. Вместо использования Linq Skip(). Take() - который записывает код, который вы показывали, - я явно пишу SQL как:

select top NTake ... from ... order by ... where orderedByValue > lastRetrievedValue 

этот работает быстро (учитывая, что у меня есть индекс по упорядоченному столбцу (столбцам)).

Ответ 4

В вашей таблице может отсутствовать какой-либо индекс (или у вас их слишком много), что приводит к тому, что упорядочение/фильтрация SQL не может эффективно пропускать много строк (или в случае слишком большого количества индексов, выбирая хороший индекс для задания).

Попробуйте напрямую протестировать SQL-запрос:

  • проверить его фактический план выполнения,
  • проверьте отсутствие подсказок для индекса (может потребоваться переписать запрос как нединамический SQL-запрос, если ef выпустил некоторый динамический код запроса),
  • проверьте, чтобы сортировки разливались в temp db,
  • ...

Итак, вкратце, проверьте, действительно ли проблема является проблемой Entity-Framework или "чистой" проблемой SQL.

Боковое примечание: EF выдает offset/fetch запрограммированные запросы только в том случае, если он настроен для диалекта SQL2012. Для предыдущих диалектов вместо этого используется row_number().