Oracle
 sql >> Database >  >> RDS >> Oracle

Oracle 19c Open_cursor ha superato il problema

Non posso dirti cosa sta causando il tuo problema con i cursori di apertura massima, ma ti dico come trovare la causa identificando le sessioni correlate e l'istruzione SQL usando GV$OPEN_CURSOR .

Se sei fortunato puoi trovare immediatamente il problema con una semplice query che conta il numero di cursori aperti per sessione. Ci sono molte colonne nella query seguente, usa un IDE in modo da poter sfogliare facilmente tutti i dati. Nella mia esperienza, basta dare un'occhiata a colonne come USER_NAME e SQL_TEXT per identificare il colpevole.

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;

Tieni presente che ci saranno molte strane query in quella vista che potrebbero aumentare i conteggi del previsto. Con tutte le query ricorsive e memorizzate nella cache, non è insolito che una sessione "noiosa" utilizzi 50 cursori. Stai cercando sessioni con centinaia di cursori aperti. (A meno che qualcuno non abbia scioccamente abbassato il valore del parametro al di sotto del valore predefinito.)

Sfortunatamente, GV$OPEN_CURSOR non contiene dati storici e questi problemi possono avviarsi e interrompersi rapidamente se c'è un'eccezione all'interno di un ciclo stretto che apre rapidamente molti cursori. Il blocco PL/SQL seguente viene eseguito finché non trova una sessione con un numero elevato di cursori aperti, archivia i dati ed esce. Questo blocco PL/SQL è costoso e consumerà un'intera sessione di elaborazione in attesa del momento giusto, quindi usalo solo una volta per trovare il problema.

--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;

Se vuoi testare il monitoraggio, puoi usare il blocco PL/SQL sottostante per aprire un gran numero di cursori.

--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;
/