ResultSet.next очень медленный, только если запрос содержит ограничение FIRST_ROWS или ROWNUM

Я выполняю собственный запрос, используя

entityManager.createNativeQuery(sqlQuery);
query.setMaxResults(maxResults);

List<Object[]> resultList = query.getResultList();

Чтобы ускорить запрос, я подумал включить подсказку FIRST_ROWS(n) или ограничение с помощью WHERE ROWNUM > n.

Используя инструменты, я вижу, что действительно OraclePreparedStatement.executeQuery быстрее, но гораздо больше времени тратится на EJBQueryImpl.getResultList, что приводит к очень низкой производительности в целом. При более подробном рассмотрении я вижу, что каждый 10-й вызов ResultSet.next() занимает примерно столько же времени, сколько и сам executeQuery(). Это странное поведение прекращается, когда я пропускаю подсказку запроса или условие ROWNUM, тогда каждый 10-й вызов resultset.next несколько меньше, чем другие, но всего 2 мс вместо 3 секунд.


person stracktracer    schedule 14.01.2012    source источник


Ответы (3)


arrow_upward
2
arrow_downward

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

Когда вы выполняете запрос в Oracle, база данных обычно не материализует весь результирующий набор в любой момент времени (очевидно, это может потребоваться, если вы укажете предложение ORDER BY, которое требует, чтобы все данные были материализованы до того, как произойдет сортировка). Фактически Oracle не начинает материализовывать данные до тех пор, пока клиент не начнет извлекать данные. Он выполняет достаточно запроса, чтобы сгенерировать столько строк, которые клиент запросил для выборки (которых, похоже, 10 в вашем случае), возвращает эти результаты клиенту и ждет, пока клиент запросит дополнительные данные, прежде чем продолжить обработку. запрос.

Похоже, когда включена подсказка FIRST_ROWS, план запроса меняется таким образом, что его выполнение становится более дорогим. Очевидно, что это не цель подсказки FIRST_ROWS. Цель состоит в том, чтобы указать оптимизатору сгенерировать план, который сделает выборку первых N строк более эффективной, даже если это сделает выборку всех строк из запроса менее эффективной. Это приводит к тому, что оптимизатор отдает предпочтение таким вещам, как сканирование индекса, а не сканирование таблицы, где сканирование таблицы в целом может быть более эффективным. Однако в вашем случае кажется, что оценки оптимизатора неверны, и в конечном итоге он выбирает план, который обычно менее эффективен. Это часто означает, что некоторые статистические данные по некоторым объектам, на которые ссылается ваш запрос, являются неполными или неверными.

person Justin Cave    schedule 14.01.2012
comment
Спасибо. Меня не интересуют строки после n, поэтому я использую setmaxresults. Проблема также возникает при использовании ROWNUM < n вместо подсказки запроса, поэтому его следует оптимизировать для возврата всех n строк, которые когда-либо могут быть возвращены запросом с этим ограничением. Да, у меня разные планы выполнения, но, например, в разработчике SQL возврат n строк выполняется очень быстро, а при использовании с eclipselink продолжительность, кажется, умножается на n/10. - person stracktracer; 15.01.2012
comment
@stracktracer - Можете ли вы опубликовать запрос и различные планы запросов, которые вы получаете? Идентичен ли план SQL Developer плану eclipselink? SQL Developer по умолчанию возвращает только первые 50 строк данных. Вы уверены, что просите SQL Developer получить все данные и измерить это время, а не просто измерить время, необходимое для получения первых 50 строк? - person Justin Cave; 15.01.2012

arrow_upward
2
arrow_downward

Похоже, вы сделали JDBC executeQuery быстрее, но JDBC ResultSet медленнее. Вы ускорили выполнение запроса, но замедлили выборку данных. Кажется, это проблема JDBC, а не EclipseLink, вы получите тот же результат через необработанный JDBC, если вы действительно извлечете данные.

10 — это размер выборки по умолчанию, поэтому вы можете попробовать увеличить его.

См. http://www.eclipse.org/eclipselink/api/2.3/org/eclipse/persistence/config/QueryHints.html#JDBC_FETCH_SIZE

person James    schedule 16.01.2012
comment
Увеличенный размер выборки. Теперь медленных next меньше, но, к сожалению, оставшиеся становятся пропорционально длиннее, так что общая продолжительность не влияет. Если я установлю размер выборки равным числу ROWNUM, только выполнение запроса займет очень много времени. - person stracktracer; 17.01.2012

arrow_upward
0
arrow_downward

Попробуйте добавить максимальное количество строк непосредственно в SQL вместо использования setMaxResults, т.е. добавьте, где rownum ‹ maxResults к строке sql. EclipseLink будет использовать rownum в запросе на максимальное количество строк при создании SQL, но, поскольку вы используете свой собственный SQL, он будет использовать результирующий набор для ограничения количества строк.

person Chris    schedule 16.01.2012