Страницы

пятница, 3 апреля 2015 г.

ПРАКТИЧЕСКОЕ АДМИНИСТРИРОВАНИЕ. library cache

Введение

Ожидание «library cache pin». Рано или поздно, каждый администратор, в процессе мониторинга базы данных сталкивается с данным событием. На высоконагруженных системах массовое возникновение длительного ожидания «library cache pin» является довольно неприятным сигналом, говорящим о том, что нормальное функционирование экземпляра нарушено. В большинстве случаев это происходит тогда, когда над каким-то объектом базы данных, в моменты его интенсивного использования сеансами осуществляются административные действия. Возникшая ситуация обычно исправляется либо уничтожением сеанса, который явился первопричиной критической обстановки, либо ожиданием того момента времени, когда используемый объект будет освобождён другими сеансами. Болезненных последствий в этом случае для системы обычно не возникает. Правда, иногда ожидание «library cache pin» может переходить в неуправляемый лавинообразный процесс. Это особенно ярко проявляется в моменты переопределения объектов с множественными зависимостями. В этом случае для восстановления работоспособности системы приходиться массово уничтожать сеансы, в том числе и те которые не генерируют это ожидание.
Так что же такое library cache pin? Попробуем на примерах разобраться в том, что представляет собой это ожидание и чем его массовое возникновение так опасно для нормальной работы СУБД.

Защёлка

Library cache pin – это событие ожидания одноимённой защёлки. Оно возникает при первоначальной загрузке объекта в библиотечный кэш, а также тогда, когда сеанс хочет изменить или проверить объект в памяти библиотечного кэша. Обычно это происходит в случаях выполнения, разбора объекта, его переопределения или изменения привилегий доступа к нему. В эти моменты происходит либо загрузка в память библиотечного кэша динамических областей связанных с объектом или же использование этих структур, если объект уже загружен. Для того чтобы запретить одновременное изменение этих структур другими сеансами и держать библиотечный кэш в согласованном виде, сеанс должен предварительно приобрести защёлку library cache pin, причём сразу после другой защёлки library cache lock, так же связанной с доступом сеанса в библиотечный кэш. Вообще-то эти две защёлки всегда тесно связаны друг с другом. Если library cache lock управляет доступом между сеансами к объектам в библиотечном кэше, то library cache pin обеспечивает конкурентный доступ в пределах самого библиотечного кэша.
Защёлка library cache pin может захватываться в разделяемом и монопольном режимах. Разделяемый режим используется для чтения структур объекта библиотечного кэша, без права их изменения. Обычно это выполнение или исследование SQL или PL/SQL объекта. Защёлка в монопольном режиме, наоборот, приобретается для изменения информации в динамических областях объекта библиотечного кэша. Потребность в этом возникает в случаях разбора объекта, его переопределения, компиляции, а также при изменении привилегий к нему. Некоторые из этих действий совершаются сеансами самих пользователей, другие выполняются только администраторами баз данных.
Пожалуй, здесь мы ненадолго остановимся и для лучшего понимания механизма работы защёлки library cache pin немного коснёмся устройства самого библиотечного кэша.

Библиотечный кэш

Библиотечный кеш расположен в памяти разделяемого пула и в общем случае состоит из связанного набора довольно сложных структур. В самом верху этого набора расположена так называемая хэш-таблица, которая представляет собой массив бакетов (ячеек таблицы). Каждый бакет имеет свой индекс и содержит адрес начала двунаправленного списка дескрипторов объектов библиотечного кэша (Library Cache Object Handle). Дескрипторы в свою очередь указывают на структуры самих объектов библиотечного кэша (Library Cache Object или LCO). Обычно в бакете расположен список с всего одним дескриптором LCO, но бывает и так, что в результате так называемой хэш-коллизии в список указателей может попадать и несколько объектов.
Объект библиотечного кэша представляет собой довольно сложную структуру. В эту структуру входит имя объекта, представляющее собой либо действительное имя объекта, либо текст SQL курсора или PL/SQL блока. В структуру входит также хэш значение, пространство имён (namespace), время изменения объекта (timestamp), таблица зависимостей, таблица дочерних курсоров, таблица разрешений, списки ожиданий, тип объекта, флаг состояний, статус и блоки данных. Из всего этого набора составных частей LCO нас больше будут интересовать блоки данных. Почему? Об этом немного позже. А пока…
Объект библиотечного кэша может содержать до восьми блоков данных, на которые ссылается с помощью дескрипторов. Каждый блок имеет заголовок, содержащий флаг статуса и счётчик pin, указывающий на количество закреплений данного блока. Информация, хранимая в блоках данных, разнообразна и зависит от типа объекта библиотечного кэша. С этим же связано так же то, что в структуре LBO могут присутствовать не все блоки данных. Ниже представлена краткая информация о содержимом блоков данных LBO:
  • Блок 0 – Object. Информация о самом объекте;
  • Блок 1 – Source. Исходный код объекта;
  • Блок 2 – DIANA. Это промежуточный код PL/SQL объекта;
  • Блок 3 - PCODE;
  • Блок 4 – MCODE. Здесь расположен машинно-зависимый код PL/SQL объекта;
  • Блок 5 - Errors;
  • Блок 6 – SQL Context. Здесь обычно храниться план выполнения;
  • Блок 7 - Free;
И так, теперь мы в общих чертах представляем, как устроен библиотечный кэш Oracle и знаем, для чего в системе нужна защёлка library cache pin. Можно приступать к практической части статьи. Но прежде чем это делать, пройдёмся немного по описанию динамических таблиц, непосредственно связанных с библиотечным кэшем.

Динамические таблицы

Из всего списка динамических таблиц Oracle касающихся библиотечного кэша, пожалуй, можно выделить всего три таблицы. Это таблица x$kgllk, которая содержит все структуры блокировок библиотечного кэша, таблица x$kglpn, содержащая структуры состояний закреплений в библиотечном кэше и таблица x$kglob, хранящая в себе все ресурсы, находящиеся в библиотечном кэше. Так же в процессе изучения нам понадобятся ещё две вспомогательных динамические таблицы x$ksuse и x$ksled. Первая из них хранит всю информацию о текущих сеансах Oracle, вторая, наименования всех ожиданий Oracle.
Столбцов у этих динамических таблиц много, поэтому ограничимся только теми полями, которые нам понадобятся для изучения.
x$kgllk - блокировки библиотечного кэша
  • kgllkuse - адрес сеанса;
  • kgllkhdl – дескриптор KGL объекта (объекта в Kernel Generic Library cache);
  • kgllkcnt - количество блокировок объекта;
  • kgllkmod – режим захвата блокировки;
  • kgllkreq– режим запроса блокировки;
x$kglpn – закрепления в библиотечном кэше
  • kglpnhdl - дескриптор KGL объекта;
  • kglpnuse - адрес сеанса;
  • kglpncnt - количество закреплений объекта в сеансе (включая зависимые объекты);
  • kglpnmod – режим захвата закрепления объекта;
  • kglpnreq – режим запроса закрепления объекта;
x$kglob –ресурсы библиотечного кэша
  • kglhdadr – дескриптор KGL объекта;
  • kglnaown – владелец ресурса;
  • kglnaobj – имя ресурса;
x$ksuse - текущие сеансы
  • addr – адрес сеанса;
  • ksusenum – идентификатор сеанса (SID);
  • ksuudlna – имя пользователя;
  • ksuseopc – номер события;
x$ksled – наименования событий
  • indx – индекс события;
  • kslednam – наименование события;
Попробуем соединить имеющиеся в нашем распоряжении динамические таблицы, и начнем, пожалуй, с таблицы x$kglpn. Именно в ней храниться информация обо всех текущих закреплениях объектов в библиотечном кэше. Единственное чего там нет, это наименований самих этих объектов, в отличии, к примеру, от динамической таблицы x$kgllk. В то же время в таблице x$kglpn есть поле kglpnhdl. Оно содержит адрес дескриптора KGL объекта в библиотечном кэше и указывает на поле kglhdadr из таблицы x$kglob. Связав две таблицы по перечисленным выше полям, мы вычислим имя ресурса закреплённого в библиотечном кэше. В дальнейшем нам остаётся только связать таблицу x$kglpn с таблицей x$ksuse, таким образом, мы определим сеанс, осуществивший закрепление объекта в библиотечном кэше. Оставшаяся таблица x$ksled нужна нам для вывода наименования текущего события, происходящего в сеансе. Впрочем, если вместо таблицы x$ksuse использовать динамическое представление v$session, то данная таблица просто не понадобиться.
В результате полученных связок динамических таблиц мы получим запрос, который будет показывать нам все закрепления сеансов пользователей применительно к какому-то определённому объекту:
SELECT c.ksusenum sid, c.ksuudlna username, a.kglpncnt pin_cnt,
       a.kglpnmod pin_mode, a.kglpnreq pin_req, d.kslednam event
  FROM x$kglpn a, x$kglob b, x$ksuse c, x$ksled d
 WHERE b.kglhdadr = a.kglpnhdl AND a.kglpnuse = c.addr AND 
       c.ksuseopc = d.indx AND b.kglnaown = 'TEST' AND 
       b.kglnaobj = 'SLEEP_PROC'
Данным объектом в нашем случае будет тестовая процедура, которую мы создадим чуть позже, а пока приведем пример запроса для другой динамической таблицы x$kgllk:
SELECT c.ksusenum sid, c.ksuudlna username, a.kgllkcnt lck_cnt,
       a.kgllkmod lck_mode, a.kgllkreq lck_req, d.kslednam event
  FROM x$kgllk a, x$kglob b, x$ksuse c, x$ksled d
 WHERE b.kglhdadr = a.kgllkhdl AND a.kgllkuse = c.addr AND 
       c.ksuseopc = d.indx AND b.kglnaown = 'TEST' AND 
       b.kglnaobj = 'SLEEP_PROC'
Запрос предназначен для вывода информации о блокировках библиотечного кэша, выбранного нами объекта. Проще говоря, он будет выводить состояние блокировок library cache lock и в дальнейшем позволит прояснить некоторые моменты закрепления объектов. По соединению таблиц запрос аналогичен предыдущему SQL выражению, хотя здесь можно было бы обойтись и без таблиц x$kglob и x$ksuse. В таблице x$kgllk большинство полей уже имеют необходимую нам информацию.

Создание теста

После того как мы определились с запросами, которые помогут нам наблюдать состояние исследуемых защёлок, можно приступать к созданию теста. В качестве тестовой базы данных у нас будет выступать редакция Oracle Database 10G Express Edition, а в качестве клиентского приложения инструмент администратора ZhiSQL for Oracle.
Для начала создадим трёх пользователей user1, user2, user3. Они будут выступать в качестве простых клиентов, выполняющих какую-то процедуру. Пользователь user3 при этом не будет иметь права на её запуск. Далее создадим пользователя user_dba. Это будет администратор, который имеет право компилировать указанную процедуру и выполнять другие привилегированные действия. В качестве последнего действия создадим саму процедура sleep_proc расположенную в схеме test, единственным действием которой будет засыпание на 60 секунд.
И так, приступим к созданию необходимых нам тестовых объектов:
Подключение к: 
Oracle Database 10g Express Edition Release 10.2.0.1.0 – Production

SYS@XE> CREATE USER user_dba IDENTIFIED BY test; 
 
Пользователь создан

SYS@XE> GRANT DBA TO user_dba;
 
Grant succeeded

SYS@XE> CREATE USER user1 IDENTIFIED BY test; 
 
Пользователь создан

SYS@XE> CREATE USER user2 IDENTIFIED BY test; 
 
Пользователь создан

SYS@XE> CREATE USER user3 IDENTIFIED BY test;
 
Пользователь создан

SYS@XE> GRANT CONNECT TO user1, user2, user3;
 
Grant succeeded

SYS@XE> CREATE USER test IDENTIFIED BY test; 
 
Пользователь создан

SYS@XE> GRANT CONNECT, RESOURCE TO test;
 
Grant succeeded

SYS@XE> GRANT EXECUTE ON sys.dbms_lock TO test;
 
Grant succeeded

SYS@XE> CREATE OR REPLACE PROCEDURE test.sleep_proc
         2>   AS
         3> BEGIN
         4>   sys.dbms_lock.sleep(60);
         5> END;
 
Процедура изменена

SYS@XE> GRANT EXECUTE ON test.sleep_proc TO user1, user2;
 
Grant succeeded

Разделяемый режим защёлки

Тестовые объекты созданы. Теперь можно приступать и к изучению защёлки. Для начала произведём имитацию обычной работы пользователей. Образуем два сеанса от имени пользователей user1, user2 и запустим на выполнение в этих сеансах процедуру sleep_proc:
USER1@XE(38)> EXECUTE test.sleep_proc;
 
PL/SQL procedure successfully completed

USER2@XE(29)> EXECUTE test.sleep_proc;
 
PL/SQL procedure successfully completed
Процедура приостанавливает работу сеанса на 60 секунд. Этого времени нам с избытком хватит для просмотра состояния защёлки library cache pin с помощью запроса, составленного нами ранее:
SYS@XE(31)> SELECT c.ksusenum sid, c.ksuudlna username, a.kglpncnt pin_cnt,
         2>        a.kglpnmod pin_mode, a.kglpnreq pin_req, d.kslednam event
         3>   FROM x$kglpn a, x$kglob b,x$ksuse c, x$ksled d
         4>  WHERE b.kglhdadr = a.kglpnhdl AND a.kglpnuse = c.addr AND 
         5>        c.ksuseopc = d.indx AND b.kglnaown = 'TEST' AND 
         6>        b.kglnaobj = 'SLEEP_PROC'
 
SID USERNAME PIN_CNT PIN_MODE PIN_REQ EVENT            
--- -------- ------- -------- ------- -----------------
38  USER1    3       2        0       PL/SQL lock timer
29  USER2    3       2        0       PL/SQL lock timer

Выбрано: 2 строки
Запрос вывел для процедуры test.sleep_proc две строки. Уже одно это означает, что объект закреплён или наоборот ожидает закрепления. Информация в таблице x$kglpn появляется только на момент закрепления и исчезает сразу после его снятия. В столбце EVENT мы видим, что сеансы находятся в состоянии ожидания «PL/SQL lock timer». Через 60 секунд процедура закончит своё выполнение, а пока что столбец PIN_REQ показывает нам, ожидает ли сеанс закрепления объекта. В нашем случае он равен нолю, это означает, что ожидания закрепления нет. Значит, объект уже закреплён, и нам необходимо обратиться к столбцу PIN_MODE. Значение в нём указывает на режим, в котором произвелось закрепление. Сейчас здесь присутствует число 2 и это свидетельствует о том, что процедура sleep_proc была закреплена в библиотечном кэше в разделяемом режиме. Такой режим устанавливается для объекта библиотечного кэша в момент его выполнения или исследования сеансом и позволяет только читать информацию из структур LBO. При этом запрещается дальнейший захват защёлки в монопольном режиме на этом объекте.
Получение защёлки в разделяемом режиме позволяет расширить параллелизм в использовании объекта несколькими сеансами. Но разве нельзя было бы обойтись для этого приобретением только одной защёлки library cache lock? Для того, что бы прояснить этот вопрос, нам придётся сделать дамп библиотечного кэша и исследовать его структуру.





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

Дамп библиотечного кэша образуется при выполнении команды 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

Комментариев нет:

Отправить комментарий