Дамп библиотечного кэша

Дамп библиотечного кэша образуется при выполнении команды ALTER SESSION SET EVENTS 'immediate trace name library_cache level 32' и представляет собой текстовый файл, создаваемый по пути, определяемым значением параметра инициализации user_dump_dest. Число 32 в команде обозначает уровень трассировки библиотечного кэша. В нашем случае мы будем делать дамп кэша на самом детальном уровне.

И так, выполним предыдущий пример, и в момент времени, когда тестовая процедура исполняется, запустим на выполнение приведённую выше команду:

SYS@XE> ALTER SESSION SET EVENTS 'immediate trace name library_cache level 32';
 
Сеанс изменён

Образовавшийся файл дампа получился довольно большой. Поэтому здесь приведена только его часть, относящаяся к тестовой процедуре sleep_proc:

BUCKET 100173:
  LIBRARY OBJECT HANDLE: handle=29e34940 mutex=29E349F4(0)
  name=TEST.SLEEP_PROC 
  hash=6bf82e93d91932653de7a29b2243874d timestamp=01-20-2011 22:42:45
  namespace=TABL flags=KGHP/TIM/SML/[02000000]
  kkkk-dddd-llll=0000-0015-0015 lock=N pin=S latch#=1 hpc=0002 hlc=0002
  lwt=29E3499C[29E3499C,29E3499C] ltm=29E349A4[29E349A4,29E349A4]
  pwt=29E34980[29E34980,29E34980] ptm=29E34988[29E34988,29E34988]
  ref=29E349BC[29E349BC,29E349BC] lnd=29E349C8[29D4E794,29E3546C]
    LIBRARY OBJECT: object=27933f7c
    type=PRCD flags=EXS/LOC[0005] pflags=NST[0001] status=VALD load=0
    DEPENDENCIES: count=5 size=16
    dependency#    table reference   handle position flags
    ----------- -------- --------- -------- -------- -------------------
              0 27934338  279340f4 29f06c9c        0 DEP[01]
              1 27934338  27934128 29eb9670        0 DEP[01]
              2 27934338  2793415c 29ea9e1c        0 DEP[01]
              3 27934338  27934190 29ea747c        0 DEP[01]
              4 27934338  279341c4 29d3f430        0 DEP[01]
    ACCESSES: count=1 size=16
    dependency# types
    ----------- -----
              3 000c
    DATA BLOCKS:
    data#     heap  pointer    status pins change whr alloc(K)  size(K)
    ----- -------- -------- --------- ---- ------ --- -------- --------
        0 29f16f2c 27934038 I/P/A/-/-    0 NONE   00      0.89     1.05
        2 27934218 27349a68 I/-/A/-/-    0 NONE   00      6.87     8.00
        4 2793428c 27354b78 I/P/A/-/-    2 NONE   00      0.62     4.00

Вспомним устройство библиотечного кэша. В самом верху, приведенного отрезка дампа, после слова BUCKET мы видим номер бакета (его индекс в хэш-таблице). Бакет содержит список дескрипторов объектов библиотечного кэша. В нашем случае в списке находиться один дескриптор, его значение приведено чуть ниже, сразу после слова LIBRARY OBJECT HANDLE . Далее идут составные части структур объекта библиотечного кэша. Мы видим имя объекта, нашу тестовую процедуру TEST.SLEEP_PROC, название пространства имён TABL, сразу после слова namespace. В названии TABL, кстати, на самом деле нет ничего необычного. Это наименование обозначает пространство имён для определения таблиц и спецификации хранимых объектов PL/SQL.

Следом за названием пространства имён нам следует обратить внимание на значение, идущее сразу после идентификатора pin. В нашем примере оно ровно значению S и это свидетельствует о том, что объект закреплён в библиотечном кэше в разделяемом режиме. Так же, немного левее мы можем наблюдать режим защёлки library cache lock. Значение расположено сразу после lock= и равно значению N (NULL). Это означает, что защёлка library cache lock может быть захвачена другим сеансом в любом режиме, вплоть до монопольного. Данный момент мы будем наблюдать на примере чуть позже, а пока вернёмся к тексту дампа.

Далее в дампе следуют некоторые компоненты LBO, которые были перечислены при описании устройства библиотечного кэша. Идентификатор «DEPENDENCIES» - это таблица зависимостей. Блок «ACCESSES», определяет таблицу разрешений. И наконец, последнее «DATA BLOCKS» - блоки данных LBO. Остановимся на них более подробно. Видно, что для нашего объекта библиотечного кэша, процедуры sleep_proc, выделено три блока: нулевой, второй и четвёртый. В нулевом всегда храниться служебная информация об объекте. Второй выделен под информацию об PL/SQL DIANA (промежуточном) коде. Четвёртый содержит MPCODE (машинно- зависимый) код PL/SQL объекта. Кстати, в детальном 32 уровне трассировки дампа, чуть ниже информации о блоках данных, всегда, приводится содержимое этих блоков.

Если посмотреть значение строки напротив 4 блока в столбце pins, мы увидим, что оно равно 2. Это и есть закрепления в разделяемом режиме тестовой процедуры sleep_proc, которые осуществили два наших сеанса при её выполнении. Теперь всё становиться понятно. Защёлка library cache lock приобретается только на дескриптор библиотечного кэша, причём при выполнении PL/SQL объекта она не препятствует захвату LBO в монопольном режиме другим сеансом. В тоже время, защёлка library cache pin защищает критически важные блоки данных уже внутри LBO. В нашем случае это будет машинно-зависимый код. Сеансу, который хочет изменить объект библиотечного кэша достаточно захватить защёлку library cache lock в монопольном режиме на дескрипторе LBO, при этом сеансы, пытающиеся вновь выполнить или изменить этот же объект, будут ожидать освобождения защёлки. Затем сеанс делает запрос на установку защёлки library cache pin, пытаясь изменить данные в блоках данных LBO. Если блок уже закреплён, то сеанс будет ожидать освобождения его закрепления, в нашем примере это произойдёт после того как тестовая процедура sleep_proc отработает в созданных нами сеансах.

Такой режим работы последовательного захвата защёлок library cache lock и library cache pin в монопольном режиме, позволяет заранее резервировать сеансом изменения в структурах объекта библиотечного кэша, даже не смотря на то, что данные этих структур всё ещё могут использоваться другими сеансами. Посмотрим, как это будет выглядеть практически.

Монопольный режим защёлки

Монопольный режим захвата защёлки library cache pin выполняется в основном в результате прямых административных действий над объектом. Защёлка в таком режиме приобретается при изменении кода объекта, его перекомпиляции, изменения прав, сбора статистики, очистки или удаления объекта. Данный режим защёлки устанавливается всегда, когда требуется изменить информацию в структурах объекта библиотечного кэша.

Проведём небольшой тест. Образуем два сеанса. В первом, от имени пользователя user1 запустим на выполнение процедуру sleep_proc:

USER1@XE(38)> EXECUTE test.sleep_proc;

Во втором сеансе в это же время пользователь user_dba будет осуществлять административные действия – компилировать процедуру sleep_proc:

USER_DBA@XE(29)> ALTER PROCEDURE test.sleep_proc COMPILE

Посмотрим закрепления процедуры в библиотечном кэше:

SID USERNAME PIN_CNT PIN_MODE PIN_REQ EVENT            
--- -------- ------- -------- ------- -----------------
29  USER_DBA 0       0        3       library cache pin
38  USER1    3       2        0       PL/SQL lock timer
 
Выбрано: 2 строки

Видно, что сеанс пользователя user1 закрепил наш объект в разделяемом режиме (PIN MODE равно 2). В то же время сеанс пользователя user_dba пытается приобрести защёлку library cache pin на объект в монопольном режиме (PIN_REQ равен 3), но вынужден ожидать освобождения защёлки сеансом 38.

Посмотрим, что же происходит в это время с защёлкой library cache lock:

SYS@XE> SELECT c.ksusenum sid, c.ksuudlna username, a.kgllkcnt lck_cnt,
     2>        a.kgllkmod lck_mode, a.kgllkreq lck_req, d.kslednam event
     3>   FROM x$kgllk a, x$kglob b, x$ksuse c, x$ksled d
     4>  WHERE b.kglhdadr = a.kgllkhdl AND a.kgllkuse = c.addr AND 
     5>        c.ksuseopc = d.indx AND b.kglnaown = 'TEST' AND 
     6>        b.kglnaobj = 'SLEEP_PROC'
 
SID USERNAME LCK_CNT LCK_MODE LCK_REQ EVENT            
--- -------- ------- -------- ------- -----------------
38  USER1    1       1        0       PL/SQL lock timer
29  USER_DBA 1       3        0       library cache pin
 
Выбрано: 2 строки

Результат предсказуем. Защёлка library cache lock была захвачена сеансом 38 в так называемом NULL режиме, при выполнении процедуры sleep_proc. Параллельно сеанс 29 так же приобрёл защёлку на дескриптор LBO в монопольном режиме, препятствуя другим сеансам получить доступ к объекту библиотечного кэша. Кстати, проверим, так ли это. В приведённом выше примере, образуем ещё один сеанс пользователя user_dba и выполним в нём команду компиляции процедуры sleep_proc:

USER_DBA@XE(22)> ALTER PROCEDURE test.sleep_proc COMPILE

Сеанс находится в ожидании. Посмотрим, осуществил ли он запрос на приобретение защёлки library cache pin:

SID USERNAME PIN_CNT PIN_MODE PIN_REQ EVENT            
--- -------- ------- -------- ------- -----------------
29  USER_DBA 0       0        3       library cache pin
38  USER1    3       2        0       PL/SQL lock timer
 
Выбрано: 2 строки

Из полученной информации видно, что сеанс 22 не ожидает закрепления нашего тестового LBO. Тогда чего он ждёт? Выведем список блокировок library cache lock:

SID USERNAME LCK_CNT LCK_MODE LCK_REQ EVENT             
--- -------- ------- -------- ------- ------------------
29  USER_DBA 1       3        0       library cache pin 
22  USER_DBA 0       0        3       library cache lock
 
Выбрано: 2 строки

Становиться ясно, что сеанс 29 пытается откомпилировать процедуру sleep_proc, но ждёт снятия закрепления с объекта в библиотечном кэше. Для того чтобы другие сеансы не пытались выполнить или изменить объект, он выставляет защёлку на дескриптор LBO в монопольном режиме (LCK_MODE=3). Поэтому сеанс 22 , пытающийся откомпилировать процедуру в тот же момент времени, вынужден ждать освобождения защёлки library cache lock (LCK_REQ=3). В дальнейшем все сеансы, которые хотят не только изменить объект, но и выполнить его вынуждены будут ожидать освобождения этой защёлки. Такая ситуация может нарушить нормальную работу базы данных, особенно если объект часто используется.

Возникающие опасности

Хотя защёлка library cache pin сама по себе обычно не приносит большого вреда системе, так как в большинстве случаев захватывается в разделяемом режиме, её приобретение в монопольном режиме может наделать немало бед. Рассмотрим примеры.

Изменим немного нашу тестовую процедуру:

SYS@XE> CREATE OR REPLACE PROCEDURE test.sleep_proc
     2>   AS
     3> BEGIN
     4>   EXECUTE IMMEDIATE 'ALTER PROCEDURE test.sleep_proc COMPILE';
     5> END;
 
Процедура изменена

Далее образуем сеанс пользователя user_dba и запустим в нём процедуру:

USER_DBA@XE(38)> EXECUTE test.sleep_proc;

Сеанс зависает в бесконечном ожидании. Что же случилось? Все становиться понятно, если вывести список закреплений нашего тестового объекта:

SID USERNAME PIN_CNT PIN_MODE PIN_REQ EVENT            
--- -------- ------- -------- ------- -----------------
38  USER_DBA 0       0        3       library cache pin
38  USER_DBA 3       2        0       library cache pin
 
Выбрано: 2 строки

Сеанс ожидает освобождение закрепления LBO, которое он сам же и сделал. Не стоит напоминать, что вместе с ним будут ждать, и все последующие сеансы, пытающиеся выполнить эту процедуру. Ожидание такое бесконечно и может быть прервано только уничтожением сеанса.

Следующий пример не столько опасен, как предыдущий, но тоже может приводить к неприятным ситуациям. Вернём для начала процедуру sleep_proc в первоначальное состояние. Далее образуем три сеанса от имени пользователей user1, user2, user3. В первом сеансе запустим процедуру sleep_proc на выполнение:

USER1@XE(38)> EXECUTE test.sleep_proc;

В третьем сеансе, от имени пользователя user3, попытаемся переопределить процедуру. При этом не стоит забывать, что пользователь user3 не только не имеет прав на переопределение этой процедуры, но у него вообще отсутствуют права даже на её запуск. Единственные привилегии, которые он имеет это роль connect. Выполним команду:

USER3@XE(29)> CREATE OR REPLACE PROCEDURE test.sleep_proc
           2>   AS
           3> BEGIN
           4>   sys.dbms_lock.sleep(60);
           5> END;

Сеанс завис. Снова пробуем выполнить во втором сеансе от имени пользователя user2 процедуру sleep_proc:

USER2@XE(23)> EXECUTE test.sleep_proc;

Сеанс зависает. Что же происходит? Ищем виновника сложившейся ситуации. Для этого выведем список закреплений объекта:

SID USERNAME PIN_CNT PIN_MODE PIN_REQ EVENT            
--- -------- ------- -------- ------- -----------------
29  USER3    0       0        3       library cache pin
38  USER1    3       2        0       PL/SQL lock timer
 
Выбрано: 2 строки

Становиться ясно, что хотя пользователь user3 и не имеет никаких прав на переопределение объекта, защёлка library cache pin на объект LBO всё равно им приобретается. Проверка прав на доступ к объекту проводится, по-видимому, гораздо позже. В конечном счёте, такая ситуация может привести к блокировке доступа к объекту, правда только лишь на время его закрепления другими сеансами. Но осознание того, что любой пользователь, не смотря на имеющиеся привилегии, может пусть, хотя и кратковременно заблокировать доступ к объекту, довольно неприятно.

Если такие ситуации всё же случились, как диагностировать возникающие проблемы? Попробуем прояснить этот вопрос.

Диагностика

В предыдущих материалах мы рассматривали возможность просмотра списка закреплений объекта LBO в различных режимах. Но такой вид наблюдения не очень подходит для повседневного мониторинга сеансов ожидающих защёлку library cache pin. Гораздо удобнее будет пользоваться для этого динамическим представлением v$session. Если отслеживать события ожиданий активных сеансов, которые отображаются в этом представлении, можно без труда обнаружить длительное ожидание захвата защёлки library cache pin каким либо сеансом. Выполним, к примеру, во время нашего теста с монопольным режимом захвата защёлки следующий запрос:

SYS@XE> SELECT sid, serial#, username, 
     2>        p1text, p1raw, p2text, p2raw, p3text, p3
     3>   FROM v$session
     4>  WHERE status = 'ACTIVE' AND event = 'library cache pin'
     5>        AND wait_time = 0;
 
SID SERIAL# USERNAME P1TEXT         P1RAW    P2TEXT      P2RAW    P3TEXT             P3 
--- ------- -------- -------------- -------- ----------- -------- ------------------ ---
26  5       USER_DBA handle address 29D4F448 pin address 28379BD8 100*mode+namespace 301
 
Выбрано: 1 строка

В результатах запроса мы видим, что сеанс 26 ожидает захвата защёлки library cache pin. Из дополнительных параметров этого ожидания мы можем узнать много интересующей нас информации. Так, первый параметр ожидания, отображаемый в столбце p1raw, покажет нам дескриптор объекта библиотечного кэша, защелку на который пытается получить сеанс. Если нам требуется узнать имя этого объекта, мы можем сделать дополнительный запрос к динамической таблице x$kglob по значению этого дескриптора:

SYS@XE> SELECT kglnaown, kglnaobj
     2>   FROM x$kglob
     3>  WHERE kglhdadr = '29D4F448';
 
KGLNAOWN KGLNAOBJ  
-------- ----------
TEST     SLEEP_PROC
 
Выбрано: 1 строка

Второй параметр ожидания, который указывается в столбце p2raw, представляет собой адрес используемого закрепления объекта. Используя это значение, можно обратиться к динамической таблице x$kglpn по полю kglpnadr и вывести дополнительную информацию по закреплению объекта в библиотечном кэше, например режим закрепления. Впрочем, можно этого и не делать. Третий параметр, указанный в столбце p3, даёт нам эту информацию. Значение, отображённое в этом столбце, представляет собой сумму произведения кода режима защёлки на число 100 и кода пространства имён. В нашем случае в столбце находится значение 301, это означает 300 / 100 = 3 монопольный режим защёлки, и число 301 – 300 = 1, код пространства имён с названием TABL.

После того, как мы более или менее узнали дополнительную информацию о защёлке, монопольного захвата которой ожидает сеанс, можно принимать решение, продолжать ли дальше ожидание или уничтожить ожидающий сеанс. Если действие, которое должно выполниться этим сеансом всё же необходимо, можно попробовать уничтожить сеансы, из-за которых происходит ожидание. Для этого достаточно вывести список всех закреплений объекта LBO в разделяемом режиме из динамической таблицы x$kglpn, как мы это делали ранее, и провести уничтожение выбранных сеансов.

Выводы

Подведём небольшой итог изучения защёлки library cache pin и её ожидания:

  • Защёлка приобретается для защиты внутренних структур объекта библиотечного кэша, конкретней - блоков данных этого объекта (кучи памяти).
  • Защёлка имеет два режима: разделяемый и монопольный. Разделяемый режим предназначен только для чтения структур объекта библиотечного кэша и защёлка в таком режиме может приобретаться несколькими сеансами.
  • Разделяемый режим защёлки не даёт другому сеансу захватить защёлку в монопольном режиме. Этот сеанс будет ждать освобождения защёлки, столько сколько нужно. В этом ожидании не используется механизм засыпания (wait posting) защёлки.
  • Монопольный режим защёлки всегда приобретается в случаях изменения структур объекта библиотечного кэша, при этом не важно, имеются ли у сеанса привилегии на доступ к этому объекту или нет.
  • Сеанс, ожидающий закрепление объекта библиотечного кэша в монопольном режиме, будет препятствовать получению другими сеансами доступа к этому объекту. Запрет осуществляется на уровне получения защёлки library cache lock;

P.S.

В заключение хочется привести некоторые моменты, касающиеся библиотечного кэша и защёлки library cache pin которые не рассматривались в статье, но встречались в процессе изучения материала. Некоторые из них спорны или отсутствуют в старших версиях Oracle, поэтому я не стал включать их в основной тест статьи и оставляю право верить этим заметкам самому читателю:

  • Блоки данных объекта библиотечного кэша – это кучи памяти, выделенные при загрузке объекта в библиотечный кэш. Эти кучи расположены в разделяемом пуле и могут быть освобождены, если они не используются. Так называемый нулевой блок никогда не освобождается.
  • Указано, что таких блоков данных может быть до восьми. Именно столько указателей перечислено в таблице x$kglob. На самом деле, в дампе библиотечного кэша их можно встретить гораздо больше. В дампе их попадается до тринадцати.
  • В структурах объекта библиотечного кэша есть список ожиданий защёлок library cache pin. В дампе мы можем наблюдать его после наименования «pwt=». Если значения указателей в этом списке различаются друг от друга, то это означает что имеются ожидания закрепления объекта в монопольном режиме.
  • В некоторых источниках указывается, что выдача и отбор привилегий на объект, вызывает закрепление объекта в библиотечном кэше в монопольном режиме. Это утверждение верно только для версий Oracle ниже 10.1 . В версии Oracle 10.2 защёлка в монопольном режиме приобретается только при отборе прав. Поэтому, привилегии на объект теперь можно предоставлять (хотя и не желательно) даже при выполняющемся объекте LBO.
  • Закрепление объекта может иметь и так называемый NULL режим. Он должен отображаться в столбце PIN_MODE как 1. Хотя случаи, в которых приобретается защёлка в таком режиме, мне не известны.
  • Закрепленные объекты не очищаются из разделяемого пула с помощью команды ALTER SYSTEM FLUSH SHARED_POOL.
  • Обычно, ожидание защёлки “library cache pin” в разделяемом режиме в представлении v$session можно наблюдать только при значительной загрузке сервера.

Список литературы

  1. Richmond Shee, K. D. Oracle Wait Interface: A Practical Guide to Performance Diagnostics & Tuning.
  2. Riyaj Shamsudeen, Library cache locks and library cache pin waits.
  3. Tapas Kumar, ALL ABOUT SHARED POOL.
  4. James Koopmann, Oracle Library Cache.
  5. Steve Adams, Oracle8i Internal Services for Waits, Latches, Locks, and Memory