Oracle
 sql >> Base de données >  >> RDS >> Oracle

Oracle 19c Open_cursor a dépassé le problème

Je ne peux pas vous dire ce qui cause votre problème de curseurs ouverts maximum, mais je vous explique comment trouver la cause en identifiant les sessions associées et l'instruction SQL à l'aide de GV$OPEN_CURSOR .

Si vous avez de la chance, vous pouvez trouver le problème immédiatement avec une simple requête qui compte le nombre de curseurs ouverts par session. Il y a beaucoup de colonnes dans la requête ci-dessous, utilisez un IDE pour pouvoir parcourir facilement toutes les données. D'après mon expérience, il suffit de regarder des colonnes comme USER_NAME et SQL_TEXT pour identifier le coupable.

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;

Gardez à l'esprit qu'il y aura de nombreuses requêtes étranges dans cette vue qui peuvent rendre le nombre plus important que prévu. Avec toutes les requêtes récursives et mises en cache, il n'est pas rare qu'une session "ennuyeuse" utilise 50 curseurs. Vous recherchez des sessions avec des centaines de curseurs ouverts. (Sauf si quelqu'un a bêtement abaissé la valeur du paramètre en dessous de la valeur par défaut.)

Malheureusement, GV$OPEN_CURSOR ne contient pas de données historiques, et ces problèmes peuvent démarrer et s'arrêter rapidement s'il y a une exception à l'intérieur d'une boucle serrée qui ouvre rapidement de nombreux curseurs. Le bloc PL/SQL ci-dessous s'exécute jusqu'à ce qu'il trouve une session avec un grand nombre de curseurs ouverts, stocke les données et quitte. Ce bloc PL/SQL est coûteux et utilisera une session entière de traitement en attendant le bon moment, alors ne l'utilisez qu'une seule fois pour trouver le problème.

--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 vous souhaitez tester la surveillance, vous pouvez utiliser le bloc PL/SQL ci-dessous pour ouvrir un grand nombre de curseurs.

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