Jeg kan ikke fortælle dig, hvad der forårsager dit problem med maksimalt åbne markører, men jeg fortæller dig, hvordan du finder årsagen ved at identificere de relaterede sessioner og SQL-sætning ved hjælp af GV$OPEN_CURSOR
.
Hvis du er heldig kan du finde problemet med det samme med en simpel forespørgsel, der tæller antallet af åbne markører pr. session. Der er mange kolonner i nedenstående forespørgsel, brug en IDE, så du nemt kan gennemse alle data. Min erfaring er, at blot at kigge på kolonner som USER_NAME og SQL_TEXT er nok til at identificere den skyldige.
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;
Husk på, at der vil være mange mærkelige forespørgsler i den visning, der kan gøre antallet større, end du havde forventet. Med alle de rekursive og cachelagrede forespørgsler er det ikke usædvanligt at have en "kedelig" session med 50 markører. Du leder efter sessioner med hundredvis af åbne markører. (Medmindre nogen tåbeligt sænkede parameterværdien under standardværdien.)
Desværre, GV$OPEN_CURSOR
indeholder ikke historiske data, og disse problemer kan starte og stoppe hurtigt, hvis der er en undtagelse inde i en stram løkke, der hurtigt åbner en masse markører. Nedenstående PL/SQL-blok kører, indtil den finder en session med et stort antal åbne markører, gemmer dataene og afslutter. Denne PL/SQL-blok er dyr og vil bruge en hel behandlingssession og vente på det rigtige tidspunkt, så brug den kun én gang for at finde problemet.
--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;
Hvis du vil teste overvågningen, kan du bruge nedenstående PL/SQL-blok til at åbne et stort antal markører.
--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;
/