Saya tidak dapat memberi tahu Anda apa yang menyebabkan masalah kursor terbuka maksimum Anda, tetapi saya memberi tahu Anda cara menemukan penyebabnya dengan mengidentifikasi sesi terkait dan pernyataan SQL menggunakan GV$OPEN_CURSOR
.
Jika Anda beruntung, Anda dapat segera menemukan masalahnya dengan kueri sederhana yang menghitung jumlah kursor terbuka per sesi. Ada banyak kolom dalam kueri di bawah ini, gunakan IDE agar Anda dapat dengan mudah menelusuri semua data. Dalam pengalaman saya, hanya melirik kolom seperti USER_NAME dan SQL_TEXT sudah cukup untuk mengidentifikasi pelakunya.
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;
Ingatlah bahwa akan ada banyak pertanyaan aneh dalam tampilan itu yang mungkin membuat jumlah lebih besar dari yang Anda perkirakan. Dengan semua kueri rekursif dan cache, bukan hal yang aneh jika sesi "membosankan" menggunakan 50 kursor. Anda sedang mencari sesi dengan ratusan kursor terbuka. (Kecuali seseorang dengan bodohnya menurunkan nilai parameter di bawah default.)
Sayangnya, GV$OPEN_CURSOR
tidak berisi data historis, dan masalah ini dapat dimulai dan dihentikan dengan cepat jika ada pengecualian di dalam loop ketat yang dengan cepat membuka banyak kursor. Blok PL/SQL di bawah ini berjalan hingga menemukan sesi dengan sejumlah besar kursor terbuka, menyimpan data, dan keluar. Blok PL/SQL ini mahal, dan akan menghabiskan seluruh sesi pemrosesan menunggu saat yang tepat, jadi gunakan hanya sekali untuk menemukan masalahnya.
--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;
Jika Anda ingin menguji pemantauan, Anda dapat menggunakan blok PL/SQL di bawah ini untuk membuka sejumlah besar kursor.
--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;
/