sql >> Base de Datos >  >> RDS >> Oracle

Oracle 19c Open_cursor superó el problema

No puedo decirle qué está causando su problema de cursores abiertos máximos, pero le digo cómo encontrar la causa identificando las sesiones relacionadas y la declaración SQL usando GV$OPEN_CURSOR .

Si tiene suerte, puede encontrar el problema inmediatamente con una simple consulta que cuenta el número de cursores abiertos por sesión. Hay muchas columnas en la consulta a continuación, use un IDE para que pueda navegar fácilmente por todos los datos. Según mi experiencia, basta con echar un vistazo a columnas como USER_NAME y SQL_TEXT para identificar al culpable.

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;

Tenga en cuenta que habrá muchas consultas extrañas en esa vista que pueden hacer que los recuentos sean más grandes de lo que esperaba. Con todas las consultas recursivas y en caché, no es inusual tener una sesión "aburrida" que use 50 cursores. Está buscando sesiones con cientos de cursores abiertos. (A menos que alguien haya bajado tontamente el valor del parámetro por debajo del valor predeterminado).

Lamentablemente, GV$OPEN_CURSOR no contiene datos históricos, y estos problemas pueden comenzar y detenerse rápidamente si hay una excepción dentro de un ciclo cerrado que abre rápidamente muchos cursores. El siguiente bloque PL/SQL se ejecuta hasta que encuentra una sesión con una gran cantidad de cursores abiertos, almacena los datos y sale. Este bloque PL/SQL es costoso y consumirá una sesión completa de procesamiento esperando el momento adecuado, así que utilícelo solo una vez para encontrar el 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;

Si desea probar el monitoreo, puede usar el bloque PL/SQL a continuación para abrir una gran cantidad de cursores.

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