Проблематика и назначение:
Периодически Oracle разработчики сталкиваются с проблемой производительности PL/SQL кода. Возникают проблемы с тем, чтобы найти место pl/sql кода, в котором возникают проблемы.
Обычно профилирование pl/sql кода используется, когда необходимо определить проблему производительности в очень большом методе, либо когда у метода много внутренних зависимостей с большим количеством логики, а также нет понимание в каком месте метод код тормозит.
Методы решения проблемы:
В решение проблем с производительность в БД Oracle нам помогут:
- PL/SQL Developer — Популярное IDE для Oracle разработчиков.
- DBMS_PROFILE — Oracle пакет для профилирования (не будет рассматриваться в рамках данной статьи, т.к. информации достаточно на просторах интернета).
Метод(процедура) для профилирования:
В рамках данной статьи покажу всего лишь подход к профилированию, поэтому метод, который мы будем профилировать будет не большим.
Создадим метод для профилирования:
create or replace procedure check_cycle_performance(p_cycle_size in number) is
c_method_error constant number := -20000;
c_method_error_message constant varchar2(4000) := 'Cycle size should be > 0';
l_power_value number;
l_dual_value number;
begin
if p_cycle_size > 0 then
--Cycle with power calculation
for i in 1 .. p_cycle_size
loop
l_power_value := power(i, 2);
end loop;
--Cycle with switching context(sql-pl/sql)
for i in 1 .. p_cycle_size
loop
select i into l_dual_value from dual;
end loop;
else
raise_application_error(c_method_error, c_method_error_message);
end if;
end check_cycle_performance;
Профилирование через IDE PL/SQL Developer:
В pl/sql Developer запускаем Test Window:
Рисунок 1 — Запуск Test Window
В тестовом окне наберём код для запуска метода check_cycle_performance, заполним входную переменную l_cycle_size и нажмем кнопку для запуска профилирования:
Рисунок 2 — Запуск профилирования в PL/SQL Developer
После запуска выйдет диалоговое окно, которое предложит создать таблицу для профайлера, соглашаемся.
Далее по F8 запускаем метод, после его выполнения переходим во вкладку Profiler:
Рисунок 3 — Окно профайлера в PL/SQL.
Из рисунка 3 видно, что основная проблема по производительности состоит в строке 16.
Не трудно понять, что основная проблема производительности кроется в переключениях контекста SQL — PL/SQL.
При этом возведение в степень: строка 11, занимает гораздо меньше времени.
По мерам предотвращения переключения контекстов рассмотрим в рамках другой статьи.
Комментарии (8)
oleg_agapov
02.10.2017 12:25А это профилирование изолированно от реальных данных?
ympukhov Автор
02.10.2017 12:28Конкретно этот пример был показан для выявления проблем на тестовой/разработческой среде. Можно конкретнее в чем заключается вопрос?
oleg_agapov
02.10.2017 12:58Ну то есть, если у меня уже есть написанные процедуры и я хочу найти в них слабое место. Выполнение профилирования отразится на таблицах, которые учавствуют в процедуре? Или это будет сделано без коммита данных?
ympukhov Автор
02.10.2017 13:14Это зависит от того как ты сам управляешь транзакцией: если у тебя где-либо в коде стоит фиксация транзакции, то данные естественно зафиксируются, если нет — то нет. Если нет фиксации транзакций, то можешь в тестовом окне(рисунок 2) поставить rollback.
xtender
05.10.2017 01:26В принципе, PL/SQL Developer использует тот же DBMS_PROFILER (кстати, есть еще иерархический профайлер ).
Для большей автоматизации иногда удобнее пользоваться своими запросами к этим же таблицам. Вот рыба для них:
select r.runid ,r.related_run ,r.run_owner ,r.run_date ,r.run_comment ,r.run_total_time ,r.run_system_info ,r.run_comment1 ,u.unit_number ,u.unit_type ,u.unit_owner ,u.unit_name ,d.line# ,s.TEXT ,u.total_time ,d.total_occur ,d.total_time ,d.min_time ,d.max_time from plsql_profiler_runs r join plsql_profiler_data d on r.runid = d.runid join plsql_profiler_units u on d.runid = u.runid and d.unit_number = u.unit_number left join all_source s on u.unit_owner = s.OWNER and u.unit_name = s.NAME and u.unit_type = s.TYPE and d.line# = s.LINE where 1=1 -- and r.run_owner = user -- запуски от текущей юзера -- and r.runid = (select max(runid) from plsql_profiler_runs where run_owner=user) -- последний запуск order by r.runid,r.related_run,u.unit_number,d.line#;
YurySS
Спасибо! Много лет пользуюсь PL/SQL Developer, а о такой возможности узнал только сейчас.