Скорость постраничных запросов в Oracle - PullRequest
10 голосов
/ 17 мая 2011

Это бесконечная тема для меня, и мне интересно, могу ли я что-то упустить из виду.По сути, я использую два типа операторов SQL в приложении:

  1. Регулярные запросы с пределом «отката»
  2. Сортированные и постраничные запросы

Сейчас,мы говорим о некоторых запросах к таблицам с несколькими миллионами записей, объединенных еще в 5 таблиц с несколькими миллионами записей.Ясно, что мы вряд ли хотим получить их все, поэтому у нас есть два вышеупомянутых метода для ограничения пользовательских запросов.

Случай 1 действительно прост.Мы просто добавляем дополнительный фильтр ROWNUM:

WHERE ...
  AND ROWNUM < ?

Это довольно быстро, поскольку CBO Oracle примет этот фильтр во внимание для своего плана выполнения и, вероятно, применяет операцию FIRST_ROWS (аналогично принудительнойпо подсказке /*+FIRST_ROWS*/.

Случай 2 , однако немного сложнее с Oracle, так как нет условия LIMIT ... OFFSET, как в других RDBMS. Поэтому мы вкладываем наш "бизнес""запрос в технической оболочке как таковой:

SELECT outer.* FROM (
  SELECT * FROM (
    SELECT inner.*, ROWNUM as RNUM, MAX(ROWNUM) OVER(PARTITION BY 1) as TOTAL_ROWS
    FROM (
      [... USER SORTED business query ...]
    ) inner
  ) 
  WHERE ROWNUM < ?
) outer
WHERE outer.RNUM > ?

Обратите внимание, что поле TOTAL_ROWS рассчитано, чтобы узнать, сколько страниц у нас будет, даже без извлечения всех данных. Теперь этот пейджинговый запрос обычно вполне удовлетворителен.Но время от времени (как я уже сказал, при запросе записей 5M +, возможно, включая неиндексированные поиски), это выполняется в течение 2-3 минут.

EDIT : обратите внимание, что потенциальная возможностьУзкое место не так легко обойти, потому что сортировка, которая должна быть применена перед подкачкой!

Интересно, это современное моделирование LIMIT ... OFFSET, включая TOTAL_ROWS вOracle,или есть лучшее решение, которое будет быстрее по конструкции, например, используя оконную функцию ROW_NUMBER() вместо псевдостолбца ROWNUM?

Ответы [ 4 ]

6 голосов
/ 17 мая 2011

Основная проблема в случае 2 заключается в том, что во многих случаях весь набор результатов запроса должен быть получен и затем отсортирован до , могут быть возвращены первые N строк - если только столбцы ORDER BY не проиндексированы и Oracle Можно использовать индекс, чтобы избежать сортировки. Для сложного запроса и большого набора данных это может занять некоторое время. Однако могут быть некоторые вещи, которые вы можете сделать, чтобы улучшить скорость:

  1. Постарайтесь убедиться, что во внутреннем SQL не вызывается никаких функций - их можно вызывать 5 миллионов раз просто для возврата первых 20 строк. Если вы можете переместить эти вызовы функций во внешний запрос, они будут вызываться поменьше.
  2. Используйте подсказку FIRST_ROWS_n, чтобы подтолкнуть Oracle к оптимизации, так как вы никогда не вернете все данные.

EDIT:

Еще одна мысль: вы в настоящее время предоставляете пользователю отчет, который может вернуть тысячи или миллионы строк, но пользователь никогда не будет реально просматривать их все. Можете ли вы не заставлять их выбирать меньшее количество данных, например, ограничивая выбранный диапазон дат 3 месяцами (или как угодно)?

3 голосов
/ 18 мая 2011

«LIMIT ... OFFSET» - в значительной степени синтаксический сахар. Это может сделать запрос более привлекательным, но если вам все еще нужно прочитать весь набор данных, отсортировать его и получить строки "50-60", то это работа, которую нужно сделать.

Если у вас есть индекс в правильном порядке, то это может помочь.

3 голосов
/ 17 мая 2011

Возможно, вы захотите отследить запрос, который занимает много времени, и посмотреть на его план объяснения.Скорее всего, узкое место в производительности возникает из расчета TOTAL_ROWS.Oracle должен прочитать все данные, даже если вы выбираете только одну строку, это общая проблема, с которой все СУБД сталкиваются с этим типом запроса.Никакая реализация TOTAL_ROWS не сможет обойти это.

Радикальный способ ускорить этот тип запроса - отказаться от вычисления TOTAL_ROWS.Просто покажите, что есть дополнительные страницы.Ваши пользователи действительно должны знать, что они могут пролистать 52486 страниц?Оценка может быть достаточной.Вот еще одно решение, реализованное в поиске Google, например: подсчитать количество страниц вместо того, чтобы фактически считать их.

Разработка точного и эффективного алгоритма оценки может быть не тривиальной.

1 голос
/ 18 мая 2011

Может быть лучше выполнить два запроса вместо попытки count () и вернуть результаты в одном запросе. Oracle может ответить на count () без какой-либо сортировки или объединения со всеми таблицами (исключение объединяемых таблиц на основе объявленных ограничений внешнего ключа). Это то, что мы обычно делаем в нашем приложении. Для важных для производительности заявлений мы напишем отдельный запрос, который, как мы знаем, вернёт правильное количество, поскольку иногда мы можем работать лучше, чем Oracle.

Кроме того, вы можете сделать компромисс между производительностью и актуальностью данных. Возвращение первых 5 страниц будет почти таким же быстрым, как и возвращение первой страницы. Таким образом, вы можете рассмотреть возможность сохранения результатов из 5 страниц во временной таблице вместе с датой истечения срока действия для информации. Возьмите результат из временной таблицы, если он действителен. Поместите фоновую задачу для периодического удаления просроченных данных.

...