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()
.