Oracle
 sql >> Datenbank >  >> RDS >> Oracle

Oracle 19c Open_cursor hat das Problem überschritten

Ich kann Ihnen nicht sagen, was Ihr Problem mit maximal geöffneten Cursorn verursacht, aber ich sage Ihnen, wie Sie die Ursache finden, indem Sie die zugehörigen Sitzungen und SQL-Anweisungen mit GV$OPEN_CURSOR identifizieren .

Wenn Sie Glück haben, finden Sie das Problem sofort mit einer einfachen Abfrage, die die Anzahl der offenen Cursor pro Sitzung zählt. Die folgende Abfrage enthält viele Spalten. Verwenden Sie eine IDE, damit Sie alle Daten einfach durchsuchen können. Meiner Erfahrung nach reicht ein Blick auf Spalten wie USER_NAME und SQL_TEXT aus, um den Übeltäter zu identifizieren.

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;

Denken Sie daran, dass es in dieser Ansicht viele seltsame Abfragen geben wird, die die Anzahl größer machen können, als Sie erwartet haben. Bei all den rekursiven und zwischengespeicherten Abfragen ist es nicht ungewöhnlich, dass eine "langweilige" Sitzung 50 Cursor verwendet. Sie suchen nach Sitzungen mit Hunderten von offenen Cursorn. (Es sei denn, jemand hat den Parameterwert dummerweise unter den Standardwert gesenkt.)

Leider GV$OPEN_CURSOR enthält keine historischen Daten, und diese Probleme können schnell beginnen und aufhören, wenn es eine Ausnahme innerhalb einer engen Schleife gibt, die schnell viele Cursor öffnet. Der folgende PL/SQL-Block wird ausgeführt, bis er eine Sitzung mit einer großen Anzahl offener Cursor findet, die Daten speichert und beendet wird. Dieser PL/SQL-Block ist teuer und verbraucht eine ganze Verarbeitungssitzung, die auf den richtigen Moment wartet. Verwenden Sie ihn also nur einmal, um das Problem zu finden.

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

Wenn Sie die Überwachung testen möchten, können Sie den folgenden PL/SQL-Block verwenden, um eine große Anzahl von Cursorn zu öffnen.

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