Не мога да ви кажа какво причинява проблема ви с максимално отворени курсори, но ви казвам как да намерите причината, като идентифицирате свързаните сесии и SQL оператор с помощта на GV$OPEN_CURSOR
.
Ако имате късмет, можете веднага да откриете проблема с проста заявка, която отчита броя на отворените курсори на сесия. Има много колони в заявката по-долу, използвайте IDE, за да можете лесно да разглеждате всички данни. Според моя опит, само един поглед към колони като USER_NAME и SQL_TEXT е достатъчен, за да идентифицира виновника.
select count(*) over (partition by inst_id, sid) cursors_per_session, gv$open_cursor.*
from gv$open_cursor
order by cursors_per_session desc, inst_id, sid;
Имайте предвид, че в този изглед ще има много странни заявки, които могат да направят броя по-голям, отколкото сте очаквали. С всичките рекурсивни и кеширани заявки, не е необичайно една „скучна“ сесия да използва 50 курсора. Търсите сесии със стотици отворени курсори. (Освен ако някой глупаво не е намалил стойността на параметъра под стандартната.)
За съжаление, GV$OPEN_CURSOR
не съдържа исторически данни и тези проблеми могат да започнат и да спрат бързо, ако има изключение в стегнат цикъл, който бързо отваря много курсори. Следният PL/SQL блок се изпълнява, докато не намери сесия с голям брой отворени курсори, съхрани данните и излезе. Този PL/SQL блок е скъп и ще използва цяла сесия на обработка в очакване на подходящия момент, така че го използвайте само веднъж, за да откриете проблема.
--Create table to hold the results.
create table too_many_cursors as
select 1 cursors_per_session, gv$open_cursor.*
from gv$open_cursor
where 1 = 0;
--Write the open cursor data when a session gets more than N open cursors.
declare
v_open_cursor_threshold number := 50;
v_count number;
begin
--Loop forever until the problem is found.
loop
--Count the largest numbe of open cursors.
select max(the_count)
into v_count
from
(
select count(*) the_count
from gv$open_cursor
group by inst_id, sid
);
--If the threshold is reached, write the data, commit it, and quit the program.
if v_count >= v_open_cursor_threshold then
insert into too_many_cursors
select *
from
(
select count(*) over (partition by inst_id, sid) cursors_per_session, gv$open_cursor.*
from gv$open_cursor
)
where cursors_per_session >= v_open_cursor_threshold;
commit;
exit;
end if;
end loop;
end;
/
--Your problem should now be in this table:
select * from too_many_cursors;
Ако искате да тествате наблюдението, можете да използвате блока PL/SQL по-долу, за да отворите голям брой курсори.
--Open a large number of cursors in and wait for 20 seconds.
--(Done by creating a dynamic PL/SQL block with many "open" commands with a "sleep" at the end.
declare
v_number_of_open_cursors number := 200;
v_declarations clob;
v_opens clob;
v_sql clob;
begin
for i in 1 .. v_number_of_open_cursors loop
v_declarations := v_declarations || 'v_cursor'|| i ||' sys_refcursor;' || chr(10);
v_opens := v_opens || 'open v_cursor' || i || ' for select * from dual;';
end loop;
v_sql :=
'declare '||chr(10)||v_declarations||chr(10)||
'begin'||chr(10)||v_opens||chr(10)||
'dbms_lock.sleep(20);'||chr(10)||'end;';
--Print for debugging.
--dbms_output.put_line(v_sql);
execute immediate v_sql;
end;
/