окт 202010
 

This post is also available in: English

Днес се сблъсках с този проблем и искам да споделя решението, което открих в блога на Харалд.

Една заявка изведнъж започна да се държи странно. Вместо да минава за нула време, отнема повече от час. Интересното е, че никой не я е променял от месеци.

Очевидното в плана беше, че се прави FTS (Full Table Scan, да не се бърка с Formula Telecom Solutins ;-) ) на една от най-тлъстите ни таблици. И това се виждаше само в v$sql_plan – ако дам explain plan обещава, да мине през индекс.

Използвах SELECT * FROM TABLE(dbms_xplan.display_awr(‘&sql_id’)); и видях, че плана е сгъбен от днес. Старите планове си минаваха през индекс. Защо се е променил (и то към лошо) – така и не се разбра. Не задълбах много в тази насока. Въпроса беше как мога да я изгоня от library cache, за да се генерира нов план.

Най-драстичното решение е alter system flush shared_pool. Но това е като да си запалиш къщата, за да изгониш една мишка от нея. Да, този план вероятно ще се оправи, друг може пък да се счупи, а това ще е придружено от маса CPU докато се напарснат всички заявки. Не, благодаря.

Друго нещо, което ми хрумна, е да инвалидирам заявката като пусна някакъв празен DDL на някоя от таблиците. Варианти много – примерно да сложа коментар (и това е DDL). Алтернативно мога да set-на статистики на някоя колона (без да ги променям). Тук лошото е отново големия размах, с който се действа – инвалидира се всичко, свързано с тази таблица.

Решението, което предлага Харалд, е много елегантно. Пакета dbms_shared_pool има процедура purge, която прави точно това – изритва нещо (било то курсор, пакет, или друго) от library cache. По принцип тя е нова в 11.1, но е толкова ценна, че са я добавили в 10.2.0.4. Има, обаче, две подробности.

Първо, в нашата версия на dbms_shared_pool нямаше тази процедура. Това се оправя лесно:
@?/rdbms/admin/dbmspool

След това тествах, тествах – то не бачка. Процедурата си минава, но не прави нищо полезно. Тогава се зачетох из коментарите на блога на Харалд и намерих това:
alter session set events '5614566 trace name context forever';

След това нещата потръгнаха:
select address, hash_value from v$sqlarea where sql_id = '1mqxxf66dtgdp';
exec sys.dbms_shared_pool.PURGE('0000000A5C166350, 2363276725', 'C');

Тук параметъра C идва да покаже, че гоним курсор. По същия начин може да се изритат и други неща:
– Value Kind of Object to keep
– –– –––––––-
– P package/procedure/function
– Q sequence
– R trigger
– T type
– JS java source
– JC java class
– JR java resource
– JD java shared data
– C cursor

Ta… това е. Мисля, че е полезно. И работи, тествано е.

 Posted by at 17:56

  4 Responses to “Изчистване на единичен план от shared pool”

  1. „Нула“ време е доста относително понятие :)

    Я да питам – има ли начин да се покажат *бързо* последните 5-10-20 инсертнати (ъпдейтнати) реда в „тлъста“ таблица , обикновено почти без никакви индекси.

    Нещо като аналог на tail -n 20 „file“ . Ама да е сравнително бързо.

    т.е. нещо от рода :

    select * from xxxxx where update_date > sysdate-1/24
    (това е отчайващо бавно)

  2. Здравей.

    С една дума – не, няма начин. Също както няма начин това да се направи и във файл, между другото. В един файл мога да inser-вам редове и по средата (да не говоря за update), докато tail ще ми даде просто редовете, които са на края на файла. И докато за log файлове това върши работа, в БД нещата не стоят така.

    Една важно и често забравяно правило в релационните БД е, че подредбата на редовете не е гарантирана (btw, има същото правило и за подредбата на колонките). Това означава, че като insert-на един ред, той може да влезе (физически) в средата или дори в началото на таблицата, въпреки че стойността на колонката update_date му е по-нова от всички други. Следователно, за да изкарам редовете с най-прясна update_date (ако нямам индекс) аз трябва да изровя цялата таблица (FTS), да сортирам всички редове и да взема колкото ми трябват. А това си е тежка работа.

  3. Между другото, може да нямаме tail -n NN, но имаме нещо като tail -f
    Във версия 10g се нарича DBMS_CHANGE_NOTIFICATION. Този пакет позволява да следим, примерно, кои редове се променят или добавят (малко бъглива работа, ама работи, горе-долу). Във версия 11g нещата се разширяват и пакета вече се нарича DBMS_CQ_NOTIFICATION

  4. Мерси за отговора.
    Все се надявах, че има поне някакъв системен индекс с който може бързо да се локализира последния инсърт (за ъпдейти не съм се и надявал) , ама …..

Sorry, the comment form is closed at this time.