Динамический запрос с HibernateCritera API & Oracle - производительность
Я должен использовать Hibernate и получать данные из Oracle, но проблема в том, что количество параметров, передаваемых в запрос, не всегда одинаково.
Для простоты рассмотрим следующий запрос:
выберите COL_1, COL_2, ..., COL_N из TAB_1, где COL_1 в (?,?, ...?)
Число параметров, передаваемых в предложении, находится в диапазоне от 1 до 500. Если число составляет около 1-50, оно работает довольно быстро, но для 200 требуется несколько секунд для выполнения запроса (разбор, создание плана объяснения, выполнение запроса), Индексы созданы и использованы - это было проверено.
Запрос создается динамически, поэтому я использую Hibernate Criteria API. Для первого запроса (с> 100 параметрами) это занимает 3-5 секунд, но для следующего запроса он работает быстрее (даже если количество параметров меняется). Я хотел бы улучшить время ответа на первый запрос. Что я могу сделать в этом случае, если Hibernate является обязательным?
Я хотел бы удалить этот динамический запрос, создав несколько статических запросов в виде именованных запросов в файле XML (в этом случае эти запросы будут предварительно скомпилированы в начале). Например,
1) один запрос, если количество параметров меньше 50.
В этом случае, если у нас 30 параметров, запрос будет выглядеть так:
выберите COL_1, COL_2, ..., COL_N из TAB_1, где COL_1 в (PAR_1, PAR_2,..., PAR_30, -1, -1,..., -1?)
2) второй, если число от 50 до 100 и т. Д.
Проблема в том, что не так просто использовать именованные запросы и HQL (в JDBC это было бы просто). В HQL мы передали только список, и мы не указываем количество параметров в этом списке, т.е. фактически есть только один запрос
'from Person where id in (:person_list)'
myQuery.setParameterList("person_list", myList)
Есть ли вариант, чтобы решить это?
Кстати, я думал, что план объяснения выполняется для каждого нового запроса, например:
(a) выберите COL_1, COL_2, ..., COL_N из TAB_1, где COL_1 в (?,?, ...,?) <100> - должен быть создан план объяснения
(b) выберите COL_1, COL_2, ..., COL_N из TAB_1, где COL_1 в (?,?, ...,?) <100> - план объяснения не будет создан, поскольку он уже существует в кэше
(c) выберите COL_1, COL_2, ..., COL_N из TAB_1, где COL_1 в (?,?, ...,?) <120> - должен быть создан план объяснения (нет плана объяснения для запроса с 120 параметрами), но это занимает меньше времени по сравнению с (a), почти так же, как (b), поэтому, возможно, Oracle сможет создать этот план быстрее, если аналогичный запрос был выполнен ранее
В чем причина этого?
2 ответа
Здесь есть пара вещей. Прежде всего, вы не можете связать список IN, по крайней мере, я уверен, что вы не можете. Я подозреваю, что Hibernate использует какую-то хитрость, когда вы помещаете содержимое массива в статический список, который может использовать Oracle.
Во-вторых, если этот запрос выполняется с большим количеством различных параметров, необходимо связать переменные, иначе производительность всей базы данных пострадает.
Тем не менее, есть способ связать список IN, используя "трюк", который Том Кайт описывает в своем блоге -
http://tkyte.blogspot.com/2006/01/how-can-i.html
Код там выглядит так:
ops$tkyte@ORA10GR2> with bound_inlist
2 as
3 (
4 select
5 substr(txt,
6 instr (txt, ',', 1, level ) + 1,
7 instr (txt, ',', 1, level+1) - instr (txt, ',', 1, level) -1 )
8 as token
9 from (select ','||:txt||',' txt from dual)
10 connect by level <= length(:txt)-length(replace(:txt,',',''))+1
11 )
12 select *
13 from all_users
14 where user_id in (select * from bound_inlist);
USERNAME USER_ID CREATED
------------------------------ ---------- ---------
SYSTEM 5 30-JUN-05
OPS$TKYTE 104 20-JAN-06
Часть:
12 select *
13 from all_users
14 where user_id in (select * from bound_inlist);
В основном, где ваш запрос идет. Бит выше - это хитрость, которая разбивает строку через запятую в список значений. Вместо того, чтобы связывать список в заполнитель: txt, вам нужно преобразовать список в строку и просто связать это.
Вы уверены, что разница во времени запросов не связана с кэшированием или изменениями нагрузки на компьютере? Разбор запроса займет немного времени, но несколько секунд это много времени.
Я работал с IN(...)
запросы, имеющие до 1000 идентификаторов в этом списке; Я могу гарантировать вам, что для анализа / подготовки / кэширования оператора не требуется несколько секунд.
Hibernate действительно автоматически расширяет заданный вами список параметров, используя фактическое количество элементов в передаваемом вами списке, поэтому, если вы действительно хотите сохранить его "фиксированным" на определенном уровне, все, что вам нужно сделать, это добавить достаточно -1 для конец. Тем не менее, это, безусловно, не проблема, тем более что мы говорим об ускорении первого выполнения запроса - все равно никакие операторы не были подготовлены / кэшированы.
Вы смотрели на планы выполнения ваших запросов? Оба через план объяснения и Autotrace включены? Различаются ли они, когда в вашем списке 30 элементов и 120 элементов? Действительно ли ваш реальный запрос выглядит как "выбрать... из таблицы, где идентификатор в (...)", который вы опубликовали, или он более сложный? Я готов поспорить, что где-то между 30 и 120 элементами Oracle решает (возможно, по ошибке), что будет быстрее не использовать индекс, поэтому вы видите увеличение времени.