четверг, 13 января 2011 г.

Интерфейс событий ожидания в Oracle 10g


Введение
Интерфейс событий ожидания продолжает оставаться бесценным инструментом администраторов базы данных из-за широты и глубины предоставляемой им информации, которая помогает локализовать и устранять проблемы производительности системы. На всем протяжении этой статьи мы предполагаем, что читатель знаком с концепциями событий ожидания и интерфейсом событий ожидания в сервере базы данных Oracle. В частности, эта статья предназначена для администраторов базы данных, которые имеют опыт использования интерфейса событий ожидания в сервере Oracle 9i или более ранних версиях и хотят изучить усовершенствования, появившиеся в сервере Oracle 10g. есть существенные пробелы, которые осложняют изучение изменений в сервере Oracle 10g, касающихся событий ожидания и интерфейса событий ожидания. Например, в справочном руководстве Oracle 10g Database Reference все еще приводится список событий ожидания сервера Oracle 9i. Поэтому приложение к этой статье может быть полезно. В нем перечислены все имена событий ожидания для сервера Oracle 10g Release 10.1.0.3, а также их параметры.
Несмотря на то, что события ожидания появились еще в сервере Oracle 7, до сервера Oracle 9i включительно, в интерфейс событий ожидания было внесено не очень много изменений. Сервер Oracle 7.3 имел только 106 типов событий ожидания, в сервере Oracle 9i их число превысило 400. К тому же в сервере Oracle 9i в отчетах утилиты TKPROF появилась информация о событиях ожидания, а в v$-представлениях значения времени ожидания стали показываться в микросекундах. Но на самом деле это – несущественные изменения. В сервере Oracle 10g, с другой стороны, в интерфейсе событий ожидания появилось больше существенных изменений, чем за все предыдущие годы. Теперь имеется более 800 типов событий ожидания, их имена стали более описательными, события ожидания группируются по классам, добавлено несколько новых v$-представлений, к существующим v$-представлениям добавлены полезные столбцы, появились встроенные средства сбора статистических данных – Active Session History (история активных сеансов) и Automatic Workload Repository (автоматически управляемый репозиторий рабочей нагрузки), появилась новая концепция модели времени для наблюдения за тем, на что тратят сеансы свое время, были усовершенствованы средства трассировки сеансов.
Имена событий ожидания
До сервера Oracle 10g некоторые имена событий ожидания были весьма нечеткими и не очень полезными без просмотра значений параметров конкретного события ожидания. Например, событие ожидания "enqueue" (блокировка с очередью) могло указывать на различные ситуации от конкуренции за строки таблицы до ожидания блокировки, определенной пользователем. Имена событий ожидания в сервере Oracle 10g – более описательны по части защелок, блокировок с очередью и ожиданий занятых буферов.
В сервере Oracle 10g все еще осталось событие ожидания "latch free" (освобождение защелки), но также появилось более 26 специфических событий, связанных с защелками. Они охватывают наиболее общие защелки, за которые возможна конкуренция. Раньше, если бы мы увидели, что сеанс ждет события "latch free", то мы получили бы следующую информацию:
SQL> SELECT event, state, p1, p2, p3
  2  FROM   v$session_wait
  3  WHERE  sid = 162;

EVENT         STATE            P1      P2    P3
------------- ------- -----------  ------ -----
latch free    WAITING 15113593728      97     5
Для определения смысла параметров p1, p2 и p3 мы должны были бы выполнить запрос к представлению v$event_name:
SQL> SELECT * FROM v$event_name WHERE name = 'latch free';

EVENT# NAME       PARAMETER1      PARAMETER2      PARAMETER3
------ ---------- --------------- --------------- ---------------
     3 latch free address         number          tries
Увидев, что параметр p2 – номер защелки, для того чтобы определить ожидаемую защелку, мы должны были бы выполнить запрос к представлению v$latch:
SQL> SELECT name
  2  FROM   v$latch
  3  WHERE  latch# = 97;

NAME
--------------------
cache buffers chains
В сервере Oracle 10g мы видим просто:
SQL> SELECT event, state
  2  FROM   v$session_wait
  3  WHERE  sid = 162;

EVENT                          STATE
------------------------------ -------
latch: cache buffers chains    WAITING
Описательное имя события избавляет нас от двух шагов для определения, какая защелка стала причиной ожидания. Более детальное описание позволяет нам быстрее понять основную причину ожидания.
Имена событий ожидания, связанных с блокировками, стали в сервере Oracle 10g также более описательными. Нет больше события ожидания "enqueue" – оно было заменено 184 событиями с более детализированными именами. Раньше, если сеансы ждали блокировок с очередями, то мы должны были по параметру p1 декодировать тип блокировки:
SQL> SELECT event, state, seconds_in_wait siw
  2  FROM   v$session_wait
  3  WHERE  sid = 96;

EVENT                               STATE                      SIW
----------------------------------- ------------------- ----------
enqueue                             WAITING                     24

SQL> SELECT sid,
  2         CHR (BITAND (p1,-16777216) / 16777215) ||
  3         CHR (BITAND (p1, 16711680) / 65535) enq,
  4         DECODE (CHR (BITAND (p1,-16777216) / 16777215) ||
  5                 CHR (BITAND (p1, 16711680) / 65535),
  6                   'TX', 'Transaction (RBS)',
  7                   'TM', 'DML Transaction',
  8                   'TS', 'Tablespace and Temp Seg',
  9                   'TT', 'Temporary Table',
 10                   'ST', 'Space Mgt (e.g., uet$, fet$)',
 11                   'UL', 'User Defined',
 12                   CHR (BITAND (p1,-16777216) / 16777215) ||
 13                   CHR (BITAND (p1, 16711680) / 65535)) enqueue_name,
 14         DECODE (BITAND (p1, 65535), 1, 'Null', 2, 'Sub-Share',
 15                   3, 'Sub-Exclusive', 4, 'Share', 5, 'Share/Sub-Exclusive',
 16                   6, 'Exclusive', 'Other') lock_mode
 17  FROM   v$session_wait
 18  WHERE  sid = 96;

  SID ENQ  ENQUEUE_NAME                   LOCK_MODE
----- ---- ------------------------------ ----------
   96 TX   Transaction (RBS)              Exclusive
В сервере Oracle 10g мы получаем подробную информацию непосредственно из имени блокировки:
SQL> SELECT event, state, seconds_in_wait siw
  2  FROM   v$session_wait
  3  WHERE  sid = 143;

EVENT                               STATE                      SIW
----------------------------------- ------------------- ----------
enq: TX - row lock contention       WAITING                    495
Кроме того, в некоторых случаях параметры p1, p2 и p3 содержат дополнительную информацию, смысл которой зависит от конкретных событий ожидания, связанных с блокировками. (См. в приложении список событий ожидания и их параметров.)
В сервере Oracle 10g, кроме имен событий ожидания защелок и блокировок, более описательные имена имеют и некоторые другие события ожидания. Однако их трудно перечислить полностью из-за состояния документации сервера Oracle 10g на момент написания этой статьи. Один пример изменения другого описательного имени – событие ожидания "buffer busy waits" (ожидания занятых буферов). Ситуации, когда один сеанс ждет другой сеанс, чтобы можно было прочитать с диска нужный блок данных (код причины 130), дали более описательное имя события ожидания – "read by other session" (чтение в другом сеансе).
Классы событий ожидания
В сервере Oracle 10g события ожидания группируются по категориям (классам), которые могут помочь администраторам баз данных быстрее определить вероятную основную причину ожидания. Введены следующие категории:
Administrative
(администрирование)
Idle
(простой)
Application
(приложение)
Network
(сеть)
Cluster
(кластер)
Scheduler
(планировщик)
Commit
(фиксация)
System I/O 
(системный ввод-вывод)
Concurrency
(конкурентный доступ)
User I/O
(пользовательский ввод-вывод)
Configuration
(конфигурирование)
Other
(другие)


Около 70% событий ожидания имеют категорию Other (другие) (557 из 811 в версии 10.1.0.3), в классах ожидания наиболее часто встречаются события с полезными именами. Рассмотрим в качестве примера имена некоторых событий типа "enqueue":
SQL> SELECT   wait_class, name
  2  FROM     v$event_name
  3  WHERE    name LIKE 'enq%'
  4  AND      wait_class <> 'Other'
  5  ORDER BY wait_class;

WAIT_CLASS                     NAME
------------------------------ ----------------------------------------
Administrative                 enq: TW - contention
Administrative                 enq: DB - contention
Application                    enq: PW - flush prewarm buffers
Application                    enq: RO - contention
Application                    enq: RO - fast object reuse
Application                    enq: TM - contention
Application                    enq: TX - row lock contention
Application                    enq: UL - contention
Concurrency                    enq: TX - index contention
Configuration                  enq: ST - contention
Configuration                  enq: TX - allocate ITL entry
Configuration                  enq: SQ - contention
Configuration                  enq: HW - contention
Мы видим, что блокировки TX (блокировки строк) и ТМ (блокировки таблиц) находятся в классе Application (приложение), что разумно, так как эти события ожидания обычно возникают из-за поведения приложений. Тогда как блокировки ST (управление пространством), HW (перемещение маркера максимального уровня заполнения) и SQ (номер последовательности) находятся в классе Configuration (конфигурирование), поскольку их возникновение обычно можно уменьшить изменением параметров настройки сервера базы данных и объектов.
В класс User I/O (пользовательский ввод-вывод) входят, как и можно было ожидать, события ожидания "db file scattered read" (чтение "вразброс" в кеш буферов из файлов базы данных), "db file sequential read" (последовательное чтение из файлов базы данных), "direct path read" (чтение в режиме прямого доступа) и "direct path write" (запись в режиме прямого доступа); в то время как класс System I/O (системный ввод-вывод) включает в себя многие события ожидания, связанные с чтением и записью в оперативные и архивные журнальные файлы. Класс Commit (фиксация) имеет только один тип событий ожидания, "log file sync" (синхронизация журнальных файлов), поскольку причиной этих ожиданий являются фиксации транзакций. А класс Idle (простой) составляют различные событий ожидания, которые традиционно называются "событиями простоя", такие, как "SQL*Net message from client" (SQL*Net-сообщение от клиента). Следует заметить, что иногда такие "события простоя" фактически могут быть симптомами основной причины низкой производительности, поэтому они не должны игнорироваться без анализа.
Вообще говоря, классы ожидания помогают администратору базы данных быстрее определять основные причины проблем производительности.
Усовершенствование v$-представлений
В сервере Oracle 10g появилось довольно много новых v$-представлений, которые имеют отношение к событиям ожидания, а также были добавлены новые полезные столбцы к существующим v$-представлениям. В этом разделе мы обсудим отдельные усовершенствования. Вместе с тем, некоторые вновь появившиеся v$-представления – часть важных новых функциональных возможностей сервера Oracle 10g, поэтому они будут рассмотрены позже в соответствующих разделах.
V$EVENT_NAME
В сервере Oracle 10g к представлению v$event_name было добавлено три столбца: wait_class_id (идентификатор класса ожидания), wait_class# (номер класса ожидания) и wait_class (имя класса ожидания). Эти столбцы показывают, к какому классу ожидания относится данное событие ожидания. В предыдущем разделе мы говорили о том, как можно использовать эту информацию. Теперь в представлении v$event_name имеются следующие столбцы:
SQL> DESCRIBE v$event_name
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 EVENT#                                             NUMBER
 EVENT_ID                                           NUMBER
 NAME                                               VARCHAR2(64)
 PARAMETER1                                         VARCHAR2(64)
 PARAMETER2                                         VARCHAR2(64)
 PARAMETER3                                         VARCHAR2(64)
 WAIT_CLASS_ID                                      NUMBER
 WAIT_CLASS#                                        NUMBER
 WAIT_CLASS                                         VARCHAR2(64)


V$SQL и V$SQLAREA
Представления v$sql и v$sqlarea в сервере Oracle 10g имеют шесть новых столбцов, которые относятся к событиям ожидания:
application_wait_time
(время ожидания в классе Application)
concurrency_wait_time
(время ожидания в классе Concurrency)
cluster_wait_time
(время ожидания в классе Cluster)
user_io_wait_time
(время ожидания в классе User I/O)
plsql_exec_time
(время выполнения PL/SQL-кода)
java_exec_time
(время выполнения Java-кода)


Эти столбцы предназначены для определения времени, которое SQL-операторы тратят на выполнение PL/SQL- или Java-кода, а также на ожидание в четырех конкретных классах ожидания. Документация сервера Oracle версии 10.1.0.3 не предоставляет почти никакой информации об этих новых столбцах (следует отметить, что данный недостаток устранён в следующей редакции документации – прим. В. Бегун), тем не менее, они могут быть очень важны для диагностирования проблем производительности. Для демонстрации мы воспользуемся примером, который позволит нам восполнить пробелы в документации и проиллюстрирует смысл и режим работы новых столбцов. Предположим, что мы создаем таблицу с именем testtab, приблизительно с миллионом строк. Затем в одном сеансе мы выполняем следующий оператор без фиксации транзакции, а затем выполняем этот же оператор в другом сеансе:
SQL> UPDATE testtab SET numcol = numcol + 1 WHERE ROWNUM < 1000;
Очевидно, второй сеанс будет ждать завершения события ожидания "enqueue" (блокировка с очередью) до тех пор, пока первый сеанс не зафиксирует или не откатит транзакцию. Через некоторое время мы выполним откат транзакции в первом сеансе, а затем и во втором. После этого в третьем сеансе мы выполняем следующий оператор:
SQL> UPDATE testtab SET numcol = numcol + 1;
Используемый нами экземпляр сервера базы данных имеет маленький кеш буферов, поэтому для этого оператора будет выполнено некоторое количество операций физического ввода-вывода. После завершения выполнения операторов UPDATE мы просматриваем представление v$sqlarea:
SQL> SELECT sql_id, application_wait_time appl, concurrency_wait_time concurr,
  2         user_io_wait_time user_io
  3  FROM   v$sqlarea
  4  WHERE  sql_text LIKE 'UPDATE testtab SET numcol%';

SQL_ID             APPL   CONCURR     USER_IO
------------- --------- --------- -----------
038m56cp4am0c 178500000         0       20000
fd5mxhdbf09ny         0     10000   105040000

SQL> SELECT sql_id, sql_text
  2  FROM   v$sqlarea
  3  WHERE  sql_id IN ('fd5mxhdbf09ny','038m56cp4am0c');

SQL_ID        SQL_TEXT
------------- -------------------------------------------------------------
038m56cp4am0c UPDATE testtab SET numcol = numcol + 1 WHERE ROWNUM < 1000
fd5mxhdbf09ny UPDATE testtab SET numcol = numcol + 1
Итак, мы видим, что первый оператор (блокирующий строки) затратил 178.5 секунды (178 500 000 микросекунд), ожидая завершения событий в классе ожидания Application (приложение), и 0.02 секунды, ожидая завершения событий в классе ожидания User I/O (пользовательский ввод-вывод). Вспоминая обсуждение классов ожидания, заметим, что блокировки TX (блокировки строк) находятся в классе ожидания Application, а операции чтения из файлов данных – в классе ожидания User I/O. Для второго оператора, которому потребовалось больше дисковых чтений, показаны ~105 секунд в классе User I/O и очень небольшие затраты на конкурентный доступ.
Хотя доступная в настоящее время документация Oracle не предоставляет много информации об этих новых столбцах в представлениях v$sql и v$sqlarea, они, кажется, имеют большие перспективы для диагностирования проблем производительности выполнения запросов.
V$SESSION_WAIT_HISTORY
До сервера Oracle 9i включительно v$-представления показывали нам только самое последнее событие ожидания каждого сеанса. Хотя время ожидания может накапливаться и существенно замедлять процесс, многие ожидания по отдельности очень коротки (с человеческой точки зрения). Поэтому часто трудно перехватить информацию о произошедшем событии ожидания. Новое представление v$session_wait_history в сервере Oracle 10g помогает нам, показывая последние десять событий ожидания, которые происходили в каждом сеансе. В качестве примера приведем следующий запрос, показывающий самые последние десять событий ожидания сеанса 154:
SQL> SELECT   sid, seq#, event, wait_time, p1, p2, p3
  2  FROM     v$session_wait_history
  3  WHERE    sid = 154
  4  ORDER BY seq#;

SID SEQ# EVENT                     WAIT_TIME     P1     P2     P3
--- ---- ------------------------ ---------- ------ ------ ------
154    1 db file sequential read          28      4   3547      1
154    2 log buffer space                 18      0      0      0
154    3 log buffer space                 36      0      0      0
154    4 db file sequential read           0      4   3559      1
154    5 db file sequential read           0      4   1272      1
154    6 db file sequential read           0      4   3555      1
154    7 log buffer space                  9      0      0      0
154    8 db file sequential read           0      4   3551      1
154    9 db file sequential read           6      4   1268      1
154   10 log buffer space                  8      0      0      0
Столбец seq# должен показывать хронологическую последовательность (sequence) событий ожидания, где 1 указывает на самое последнее событие ожидания в сеансе. В нашем сервере базы данных версии 10.1.0.3 на платформе Solaris столбец seq# ведет себя по-другому (bug #4429113 – прим. В. Бегун), затрудняя определение самого последнего события ожидания. В любом случае, обратите внимание: значение этого столбца seq# отличается от значения столбца seq# в представлении v$session, которое увеличивается для каждого ожидания, произошедшего в сеансе в течение "срока жизни" сеанса.
В вышеприведенном запросе мы видим, что в самых последних ожиданиях сеанса чередуются события "db file sequential read" (последовательное чтение из файлов базы данных) и "log buffer space" (пространство в журнальном буфере). Это имеет смысл, так как SQL-операторы, которые выполнялись в сеансе, выглядели так:
INSERT INTO table1 (column1, column2)
SELECT column1, column2
FROM   table2
WHERE  ...
Используя этот список самых последних ожиданий, мы можем также получить более подробную информацию. Значения параметров p1 и p2 события "db file sequential read" указывают номера файла и прочитанного блока, поэтому мы можем быстро определить, из какого сегмента читались данные.
Столбцы представления v$session_wait_history:
SQL> DESCRIBE v$session_wait_history
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 SID                                                NUMBER
 SEQ#                                               NUMBER
 EVENT#                                             NUMBER
 EVENT                                              VARCHAR2(64)
 P1TEXT                                             VARCHAR2(64)
 P1                                                 NUMBER
 P2TEXT                                             VARCHAR2(64)
 P2                                                 NUMBER
 P3TEXT                                             VARCHAR2(64)
 P3                                                 NUMBER
 WAIT_TIME                                          NUMBER
  WAIT_COUNT                                         NUMBER


V$SESSION
К представлению v$session в сервере Oracle 10g добавлено несколько очень полезных столбцов. Добавлены столбцы из представления v$session_wait, связанные с событиями ожидания. В предыдущих версиях сервера Oracle для получения более подробной информации о сеансе, в котором имели место ожидания (такой, как текст выполнявшегося SQL-оператора), мы должны были соединить представления v$session_wait и v$session:
SQL> SELECT s.sid, w.state, w.event, w.seconds_in_wait siw,
  2         s.sql_address, s.sql_hash_value hash_value, w.p1, w.p2, w.p3
  3  FROM   v$session s, v$session_wait w
  4  WHERE  s.sid = w.sid
  5  AND    s.sid = 154;
В сервере Oracle 10g мы можем получит всю эту информацию из представления v$session:
SQL> SELECT sid, state, event, seconds_in_wait siw,
  2         sql_address, sql_hash_value hash_value, p1, p2, p3
  3  FROM   v$session
  4  WHERE  sid = 154;

SID STATE   EVENT                   SIW SQL_ADDRESS      HASH_VALUE  P1   P2  P3
--- ------- ----------------------- --- ---------------- ---------- --- ---- ---
154 WAITING db file sequential read   1 000000038551E820 3625097388   4 9813   1
К представлению v$session добавлены два дополнительных столбца, полезные для анализа событий ожидания: blocking_session (блокирующий сеанс) и blocking_session_status (статус столбца blocking_session). Столбец blocking_session содержит идентификатор сеанса (SID), удерживающего ресурс, который ждет ожидающий сеанс. Столбец blocking_session_status указывает, достоверно ли содержимое столбца blocking_session. Если значение столбца blocking_session_status – VALID, то в столбце blocking_session содержится действительный SID. Раньше, если мы видели, что сеанс ожидает ресурс, то для определения, что удерживает этот ресурс, мы должны были выполнить дополнительные запросы. Если ресурс был блокировкой "enqueue", то для определения, что удерживает ее, мы должны были выполнить запрос к представлению v$lock (иногда очень дорогостоящий запрос). Теперь мы можем узнать это намного быстрее:
SQL> SELECT sid, blocking_session, username,
  2         blocking_session_status status
  3  FROM   v$session
  4  WHERE  blocking_session_status = 'VALID';

SID BLOCKING_SESSION USERNAME STATUS
--- ---------------- -------- -----------
154              157 TSUTTON  VALID
Если мы объединим это с информацией о событии ожидания, доступной теперь в представлении v$session, мы увидим:
SQL> SELECT sid, blocking_session, username,
  2         event, seconds_in_wait siw
  3  FROM   v$session
  4  WHERE  blocking_session_status = 'VALID';

SID BLOCKING_SESSION USERNAME EVENT                          SIW
--- ---------------- -------- ------------------------------ ---
154              157 TSUTTON  enq: TX - row lock contention  318

V$SYSTEM_EVENT




Представление v$system_event для каждого имени события ожидания показывает (на уровне всей системы, начиная с запуска экземпляра) количество ожиданий, общее и среднее время ожидания. Однако такое объединение данных может исказить истинную картину, поскольку небольшое количество продолжительных ожиданий может "перекосить" эти данные. Рассмотрим следующий запрос к представлению v$system_event:
SQL> SELECT event, total_waits, time_waited, average_wait
  2  FROM   v$system_event
  3  WHERE  event = 'enq: TX - row lock contention';

EVENT                         TOTAL_WAITS TIME_WAITED AVERAGE_WAIT
----------------------------- ----------- ----------- ------------
enq: TX - row lock contention       17218     2101966          122
Мы видим, что было 17 218 ожиданий, а среднее время ожидания равно 1.22 секунды, но мы не имеем понятия, как это время ожидания распределено. Все ли эти ожидания имели примерно одинаковую продолжительность? Может быть, большинство из них длилось менее одной секунды, а несколько действительно продолжительных ожиданий существенно увеличили это среднее время? Мы не можем сказать этого, исходя из представления v$system_event. Однако в сервере Oracle 10g благодаря представлению v$event_histogram мы можем увидеть более полную картину:
SQL> SELECT event, wait_time_milli, wait_count
  2  FROM   v$event_histogram
  3  WHERE  event = 'enq: TX - row lock contention';

EVENT                         WAIT_TIME_MILLI WAIT_COUNT
----------------------------- --------------- ----------
enq: TX - row lock contention               1        833
enq: TX - row lock contention               2        635
enq: TX - row lock contention               4        372
enq: TX - row lock contention               8        395
enq: TX - row lock contention              16        781
enq: TX - row lock contention              32       3729
enq: TX - row lock contention              64       3050
enq: TX - row lock contention             128        410
enq: TX - row lock contention             256         47
enq: TX - row lock contention             512         46
enq: TX - row lock contention            1024         37
enq: TX - row lock contention            2048          3
enq: TX - row lock contention            4096       6880
Мы видим, что длительность очень немногих из ожиданий была в какой-то степени равна 1.22 секунды. Почти 60% ожиданий имели длительность меньше 0.128 секунды (большинство из них – между 16 и 64 миллисекундами), а большинство из оставшихся ожиданий – между 2.048 и 4.096 секунды (в какой-то момент времени некоторые из них завершались по тайм-ауту и начинались вновь). Таким образом, представление v$event_histogram дает нам более точную картину распределения времени ожидания, чем просто суммирование по именам событий.
Столбцы представления v$event_histogram:
SQL> DESCRIBE v$event_histogram
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 EVENT#                                             NUMBER
 EVENT                                              VARCHAR2(64)
 WAIT_TIME_MILLI                                    NUMBER
 WAIT_COUNT                                         NUMBER


V$SYSTEM_WAIT_CLASS и V$SESSION_WAIT_CLASS
Два новых представления v$system_wait_class и v$session_wait_class позволяют нам получить информацию об ожиданиях на уровне системы и сеанса соответственно, суммированную по классам ожидания. Это дает нам общее представление (без фокусирования на конкретных событиях) о том, что происходит в системе или сеансе. Эти представления примерно эквивалентны представлениям v$system_event и v$session_event, разница лишь в том, что они суммируют данные о событиях по классам ожидания. Время ожидания выражается в сотых долях секунды, начиная с запуска экземпляра для представления v$system_wait_class и создания сеанса для представления v$session_wait_class.
Следующие запросы показывают, какое время (в сотых долях секунды) было затрачено на ожидание в каждом классе для системы (начиная с запуска экземпляра) и одного конкретного сеанса (с его начала):
SQL> SELECT   wait_class, time_waited
  2  FROM     v$system_wait_class
  3  ORDER BY time_waited DESC;

WAIT_CLASS    TIME_WAITED
------------- -----------
Idle            777450022
System I/O        1261584
User I/O           116667
Configuration      116481
Application         72301
Other               12432
Commit               3496
Concurrency           319
Network                 1

SQL> SELECT   wait_class, time_waited
  2  FROM     v$session_wait_class
  3  WHERE    sid = 154
  4  ORDER BY time_waited DESC;

WAIT_CLASS    TIME_WAITED
------------- -----------
Idle               612453
User I/O             1500
Configuration          28
Commit                 11
Other                   0
Application             0
Network                 0
Так как значение времени ожидания в каждом классе, показанное в этих представлениях, является накопленным значением с момента запуска системы или сеанса, эти представления лучше всего использовать для определения ожиданий в течение какого-то периода времени, делая выборки из них и сравнивая полученные результаты. Например, вы могли получить данные для всего экземпляра в момент времени T1:
DROP TABLE swc_snap;
CREATE TABLE swc_snap
AS
SELECT wait_class, total_waits, time_waited
FROM   v$system_wait_class;
А затем, в момент времени T2, немного позже, получить суммарные ожидания в интервале от T1 до T2:
SELECT   a.wait_class, (a.time_waited - b.time_waited) tm_waited
FROM     v$system_wait_class a, swc_snap b
WHERE    a.wait_class = b.wait_class
AND      a.total_waits > NVL (b.total_waits, 0)
ORDER BY tm_waited DESC;

WAIT_CLASS       TM_WAITED
--------------- ----------
Idle                255767
Application            171
System I/O             156
User I/O                44
Other                   21
Commit                  13
Network                  1


V$ACTIVE_SESSION_HISTORY
В предыдущих версиях сервера Oracle подробная информация, показываемая в представлении v$session_wait, могла бы оказаться чрезвычайно полезной при диагностировании проблем производительности, если бы вы запрашивали это представление в нужный момент времени. Представление v$session_wait_history в сервере Oracle 10g облегчает перехват подробной информации, сохраняя последние десять событий ожидания для каждого сеанса. Но что, если вам нужна подробная информация об ожиданиях сеансов за более длительный период, имевший место в прошлом, какое-то время назад? Для этого в сервере Oracle 10g предназначены средства Active Session History (ASH, история активных сеансов). Средства ASH собирают подробную информацию об ожиданиях всех сеансов, доступную для нас в течение очень долгого времени.
В сервере Oracle 10g новый фоновый демон-процесс MMNL каждую секунду выполняет запрос к представлению (я не думаю, что это сделано именно так "запрос к представлению" – прим. В. Бегун) v$session и сохраняет информацию обо всех активных сеансах в повторно используемом буфере оперативной памяти, доступном через новое представление v$active_session_history. Время доступности этой информации зависит от активности сеансов и объема памяти, которую сервер Oracle выделил для средств ASH. Цель сервера Oracle – достичь того, чтобы, по крайней мере, несколько часов данные о сеансах были доступны в этом представлении. Представление v$active_session_history содержит большую часть детальной информации о событиях ожидания, показываемой в представлении v$session:
SQL> DESCRIBE v$active_session_history
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 SAMPLE_ID                                          NUMBER
 SAMPLE_TIME                                        TIMESTAMP(3)
 SESSION_ID                                         NUMBER
 SESSION_SERIAL#                                    NUMBER
 USER_ID                                            NUMBER
 SQL_ID                                             VARCHAR2(13)
 SQL_CHILD_NUMBER                                   NUMBER
 SQL_PLAN_HASH_VALUE                                NUMBER
 SQL_OPCODE                                         NUMBER
 SERVICE_HASH                                       NUMBER
 SESSION_TYPE                                       VARCHAR2(10)
 SESSION_STATE                                      VARCHAR2(7)
 QC_SESSION_ID                                      NUMBER
 QC_INSTANCE_ID                                     NUMBER
 EVENT                                              VARCHAR2(64)
 EVENT_ID                                           NUMBER
 EVENT#                                             NUMBER
 SEQ#                                               NUMBER
 P1                                                 NUMBER
 P2                                                 NUMBER
 P3                                                 NUMBER
 WAIT_TIME                                          NUMBER
 TIME_WAITED                                        NUMBER
 CURRENT_OBJ#                                       NUMBER
 CURRENT_FILE#                                      NUMBER
 CURRENT_BLOCK#                                     NUMBER
 PROGRAM                                            VARCHAR2(48)
 MODULE                                             VARCHAR2(48)
 ACTION                                             VARCHAR2(32)
 CLIENT_ID                                          VARCHAR2(64)
Как видите, в представлении v$active_session_history собираются важные данные из представления v$session, связанные с ожиданиями. Также собирается полезная информация о SQL-операторе, выполняемом в данное время, а также текущие номера объекта, файла и блока, к которым осуществляется доступ. Когда ожидание завершается, сервер Oracle вставляет в столбец time_waited представления v$active_session_history фактическую продолжительность ожидания.
Автоматически управляемый репозиторий рабочей нагрузки, который мы рассмотрим в следующем разделе, регулярно записывает на диск данные из представления v$active_session_history, сохраняя для каждого активного сеанса одну выборку каждые десять секунд. Поэтому информация об активных сеансах остается доступной (хотя с меньшим количеством деталей) даже после того, когда данные будут вытеснены по времени из представления v$active_session_history.
Средства ASH всегда включены, поэтому вы всегда имеете доступ к подробной информации об ожиданиях, случившихся в сеансах за прошедшие несколько часов. Это означает, что, если пользователь жалуется на проблему производительности, вы можете выполнить запрос к представлению v$active_session_history и получить сведения об этой проблеме, не включая расширенную трассировку SQL-операторов или не усиливая наблюдение за представлением v$session, дожидаясь воспроизведения проблемы.
Тем не менее, важно иметь в виду, что представление v$active_session_history заполняется выборкой из представления v$session один раз в секунду. В сеансе в течение одной секунды может произойти много разных событий ожидания, но только одно последнее событие будет зарегистрировано в представлении v$active_session_history. Поэтому средства ASH полезны для общих агрегированных запросов, а не для точного измерения конкретных событий или определения минимального или максимального времени ожидания. Говоря на языке статистики, данные, собранные средствами ASH, более точны, вероятно, на большом временном интервале и/или числе сеансов.
Например, вы могли бы выполнить запрос к представлению v$active_session_history, чтобы определить, какой процент времени за прошедшие два часа конкретная группа сеансов ждет завершения дисковых чтений. Однако же использование этого представления для определения количества ожиданий дисковых чтений за прошедшую минуту, возможно, не будет приводить к очень точным результатам.
Даже притом, что ASH-данные – это только выборка для активных сеансов, полученная информация может оказаться весьма полезной. Например, следующий запрос показывает, что сеансы, в которых выполняется приложение ARXENV, за последние два часа столкнулись с большой конкуренцией за блокировки строк:
SQL> SELECT   DECODE (session_state, 'WAITING', event, NULL) event,
  2           session_state, COUNT(*), SUM (time_waited) time_waited
  3  FROM     v$active_session_history
  4  WHERE    module = 'ARXENV'
  5  AND      sample_time > SYSDATE - (2/24)
  6  GROUP BY DECODE (session_state, 'WAITING', event, NULL),
  7           session_state;

EVENT                          SESSION_STATE COUNT(*) TIME_WAITED
------------------------------ ------------- -------- -----------
                               ON CPU             124           0
log file sync                  WAITING              2       52686
db file scattered read         WAITING              2       28254
db file sequential read        WAITING              1        6059
control file sequential read   WAITING              1        9206
SQL*Net break/reset to client  WAITING              1        9140
enq: TX - row lock contention  WAITING            922   930864016
Дополнительно к выполнению запросов к представлению v$active_session_history вы для подготовки отчетов по ASH-данным можете использовать Enterprise Manager.
Хотя средства ASH по умолчанию работают на всех серверах базы данных Oracle 10g, вам запрещается выполнять запросы к представлению v$active_session_history (или подготавливать соответствующие отчеты в среде Enterprise Manager), если вы не лицензировали диагностический пакет Diagnostic Pack.
AWR
В сервере Oracle 10g появилось еще одно важное средство, имеющее отношение к интерфейсу событий ожидания. Оно называется автоматически управляемым репозиторием рабочей нагрузки Automatic Workload Repository (AWR). Репозиторий AWR представляет собой, по существу, пакет Statspack следующего поколения. По умолчанию репозиторий AWR собирает почасовые моментальные копии информации о производительности сервера базы данных, сохраняя данные в таблицах схемы SYS. Репозиторий AWR конфигурируется автоматически, когда вы создаете сервер базы данных Oracle 10g. Вы можете также вызывать пакет dbms_workload_repository, чтобы собирать данные по требованию, очищать моментальные копии или диапазоны этих копий, изменять интервалы копирования или время хранения информации. (По умолчанию моментальные копии собираются в начале каждого часа и очищаются через семь дней.)
Репозиторий AWR собирает такие же данные, что и пакет Statspack, включая статистические данные на уровне системы, ресурсоемкие SQL-операторы, и, конечно, информацию на уровне экземпляра о событиях ожидания. Репозиторий AWR также собирает новые в сервере Oracle 10g данные, такие, как статистические данные модели времени (которую мы рассмотрим в следующем разделе). Отклоняясь от темы, скажем, что версия пакета Statspack в сервере Oracle 10g также собирает много этой новой информации, включая статистические данные модели времени.
Вы можете сгенерировать отчет репозитория AWR об активности сервера базы данных между двумя моментальными копиями, выполняя в каталоге $ORACLE_HOME/rdbms/admin скрипт awrrpt.sql. Этот скрипт генерирует отчеты, сформатированные как простой текст или в формате HTML. Если прежде вы использовали пакет Statspack, отчеты будут выглядеть привычно. Вы можете также использовать для этого Enterprise Manager.
Репозиторий AWR имеет много преимуществ по сравнению с пакетом Statspack. Например, он значительной частью интегрирован в ядро сервера Oracle, что уменьшает потребности в ресурсах и накладные расходы при сборе моментальных копий. В моментальные копии репозитория AWR в дополнение к сбору данных на уровне системы, знакомому пользователям пакета Statspack, также включаются ASH-данные из представления v$active_session_history, обеспечивая информацию на уровне сеанса.
Данные, собранные репозиторием AWR, легко доступны через представления, имена которых начинаются с DBA_HIST. Это позволяет вам генерировать собственные отчеты, которые содержат только данные, нужные в конкретной ситуации (если по каким-то причинам вы не находите в стандартном отчете репозитория AWR то, что вам нужно). Например, следующий запрос показывает идентификаторы двух самых последних моментальных копий:
SQL> SELECT snap_id, begin_interval_time, end_interval_time
  2  FROM   (
  3         SELECT   snap_id, begin_interval_time, end_interval_time
  4         FROM     dba_hist_snapshot
  5         ORDER BY end_interval_time DESC
  6         )
  7  WHERE  ROWNUM <= 2;

   SNAP_ID BEGIN_INTERVAL_TIME       END_INTERVAL_TIME
---------- ------------------------- -------------------------
       362 10-MAR-05 04.00.02.018 PM 10-MAR-05 05.00.36.581 PM
       361 10-MAR-05 03.00.25.885 PM 10-MAR-05 04.00.02.018 PM
Точно так же, как и средства ASH, репозиторий AWR по умолчанию работает на всех серверах базы данных Oracle 10g. Так же, как и в случае со средствами ASH, вам запрещено выполнять запросы к AWR-представлениям (или генерировать AWR-отчеты), если вы не лицензировали диагностический пакет Diagnostic Pack. Репозиторий AWR при сборе моментальных копий потребляет системные ресурсы и расходует память в табличном пространстве SYSAUX, если у вас не лицензировано использование репозитория AWR, вы можете выключить сбор моментальных копий AWR. Это можно сделать с помощью пакета dbms_workload_repository. Если репозиторий AWR не доступен вам, пакет Statspack остается хорошим способом работы с сервером Oracle 10g.
Статистические данные модели времени
В сервере Oracle 10g введена новая концепция – статистические данные модели времени (Time Model Statistics). Эта информация обеспечивает еще один способ наблюдения за затратами времени с большими подробностями, чем это был доступно раньше. Представление v$sys_time_model показывает статистические данные модели времени на уровне всей системы, начиная с запуска экземпляра, тогда как представление v$sess_time_model показывает статистические данные модели времени для каждого сеанса с его начала. Столбцы в этих представлениях следующие:
SQL> DESCRIBE v$sys_time_model
 Name                                     Null?    Type
 ---------------------------------------- -------- ---------------------------
 STAT_ID                                           NUMBER
 STAT_NAME                                         VARCHAR2(64)
 VALUE                                             NUMBER

SQL> DESCRIBE v$sess_time_model
 Name                                     Null?    Type
 ---------------------------------------- -------- ---------------------------
 SID                                               NUMBER
 STAT_ID                                           NUMBER
 STAT_NAME                                         VARCHAR2(64)
 VALUE                                             NUMBER
Пример запроса к представлению v$sys_time_model:
SQL> SELECT   stat_name, value / 1000000 seconds
  2  FROM     v$sys_time_model
  3  ORDER BY seconds DESC;

STAT_NAME                                           SECONDS
------------------------------------------------ ----------
DB time                                           80970.190
sql execute elapsed time                          75057.271
DB CPU                                            44448.628
background elapsed time                           29333.160
PL/SQL execution elapsed time                      8824.538
background cpu time                                5170.311
parse time elapsed                                 1270.147
hard parse elapsed time                             838.068
PL/SQL compilation elapsed time                     176.731
sequence load elapsed time                          112.334
connection management call elapsed time              44.644
failed parse elapsed time                            11.946
hard parse (sharing criteria) elapsed time            5.579
hard parse (bind mismatch) elapsed time               4.610
failed parse (out of shared memory) elapsed time      0.000
Java execution elapsed time                           0.000
inbound PL/SQL rpc elapsed time                       0.000
Этот запрос по сравнению с запросами к представлениям v$sysstat и v$sesstat дает нам намного больше информации о том, на что сеансы сервера Oracle тратят свое время (по категориям). Конечно, прежде, чем мы сможем заставить эту информацию работать на нас, мы должны знать, как интерпретировать ее. Значения в этих представлениях показываются в микросекундах, и фоновые процессы не учитываются, если только в имени статистического показателя не появляется слово "background". Статистический показатель "DB time" показывает общее затраченное время на вызовы сервера базы данных (только пользовательские процессы). В него входит время, затраченное на работу центрального процессора, и события ожидания, которые не являются событиями простоя. Описание других статистических данных модели времени см. в листинге представления v$sess_time_model в справочном руководстве Oracle 10g Database Reference.
Из этого запроса видно, среди многих других полезных фактов, что никакого времени не было затрачено на выполнение Java-кода (Java execution elapsed time), очень мало времени было затрачено на полный синтаксический разбор или компиляцию PL/SQL-кода (hard parse elapsed time или PL/SQL compilation elapsed time), фоновые процессы использовали приблизительно 10% времени центрального процессора (background cpu time), и приблизительно 11% общего затраченного времени пользовательских сеансов были истрачены на выполнение PL/SQL-кода (PL/SQL execution elapsed time).
Усовершенствования средств трассировки
Расширенное средство SQL-трассировки, называемое также отладочным событием 10046, позволяет нам собирать в трассировочном файле подробную информацию о каждом событии ожидания, случившемся в сеансе сервера базы данных. Эта средство было доступно в сервере Oracle в течение длительного времени. В сервере Oracle 10g в этой области предлагаются некоторые полезные усовершенствования.
Включение расширенной SQL-трассировки всегда было немного неудобным. В ранних версиях сервера Oracle для этого вы должны были использовать неуклюжий оператор ALTER SESSION SET EVENTS или, что еще хуже, недокументированную процедуру dbms_system.set_ev, чтобы включить событие 10046 в другом пользовательском сеансе. В сервере Oracle 8i, чтобы облегчить этот шаг, появился пакет dbms_support, но пакет отсутствовал во многих выпусках сервера Oracle и обычно не инсталлировался по умолчанию.
В сервере Oracle 10g появился новый пакет dbms_monitor. Этот пакет позволяет, среди многого другого, очень легко включать и выключать расширенную SQL-трассировку для любого сеанса сервера Oracle. Легко запоминающиеся вызовы процедур этого пакета позволяют включать и выключать расширенную SQL-трассировку с возможностью записи в трассировочный файл информации о событиях ожидания и/или переменных связывания:
SQL> DESCRIBE dbms_monitor
...
PROCEDURE SESSION_TRACE_DISABLE
 Argument Name                  Type                    In/Out Default?
 ------------------------------ ----------------------- ------ --------
 SESSION_ID                     BINARY_INTEGER          IN     DEFAULT
 SERIAL_NUM                     BINARY_INTEGER          IN     DEFAULT
PROCEDURE SESSION_TRACE_ENABLE
 Argument Name                  Type                    In/Out Default?
 ------------------------------ ----------------------- ------ --------
 SESSION_ID                     BINARY_INTEGER          IN     DEFAULT
 SERIAL_NUM                     BINARY_INTEGER          IN     DEFAULT
 WAITS                          BOOLEAN                 IN     DEFAULT
 BINDS                          BOOLEAN                 IN     DEFAULT
Если параметр session_id (идентификатор сеанса) не указывается или имеет NULL-значение, то будет трассироваться ваш собственный сеанс. Таким образом, следующие два оператора должны быть эквивалентными:
  • ALTER SESSION SET events '10046 trace name context forever, level 12';
  • EXECUTE dbms_monitor.session_trace_enable (waits=>TRUE, binds=>TRUE);
В сервере Oracle 9i и более ранних версиях расширенную SQL-трассировку было удобно использовать, если ваше приложение соединялось с сервером базы данных Oracle через соединение выделенного сервера. Если использовалась архитектура разделяемого сервера, то каждый процесс разделяемого сервера, который обслуживал запрос трассируемого сеанса, писал свои данные в отдельный трассировочный файл. Кроме того, трассировать сеансы в среде пула соединений было трудно, потому что один сеанс сервера Oracle мог обрабатывать запросы многих разных сеансов конечных пользователей.
Пакет dbms_monitor в сервере Oracle 10g позволяет разрешить эту проблему. Вместо включения расширенной SQL-трассировки конкретного сеанса сервера Oracle вы можете включить ее для конкретного идентификатора клиента или комбинации сервиса, модуля и действия. Каждый раз, когда какой-то сеанс сервера Oracle имеет указанные идентификатор клиента или комбинацию сервиса, модуля и действия, он будет трассироваться. Сеансы могут устанавливать и сбрасывать свои идентификаторы клиента с помощью пакета dbms_session, а свои модули и действия – с помощью пакета dbms_application_info.
Предположим, веб-приложение для обслуживания запросов пользователей использует пул 30 соединений с сервером базы данных и поддерживает таблицу текущих сеансов current_sessions для отслеживания состояния сеансов конечных пользователей. Когда какой-то пользователь в окне своего браузера щелкает по кнопке, сервер приложений получает HTTP-запрос и передает его процессу сервера приложений. Этот процесс захватывает свободное соединение с сервером базы данных из пула соединений и обращается к этому серверу для выполнения запроса. Вероятно, что последующие запросы этого же пользователя будут обработаны с использованием разных соединений с сервером базы данных.
В сервере Oracle 9i и более ранних версиях в этой среде было очень трудно трассировать выполнение действий в сервере базы данных для одного конкретного пользователя. Обращения пользователя к базе данных разбрасывались в пуле соединений по многим серверным процессам. Кроме того, каждый из этих процессов сервера Oracle обрабатывал запросы многих разных конечных пользователей.
С помощью пакета dbms_monitor в сервере Oracle 10g можно модифицировать приложение, чтобы существенно упростить расширенную SQL-трассировку. Мы упоминали, что приложение использует таблицу current_sessions для отслеживания состояния каждого из сеансов конечных пользователей. Каждый раз, когда сервер приложений захватывает соединение с сервером базы данных в пуле соединений, оно может (перед выполнением любого доступа к базе данных для сеанса конечного пользователя) установить идентификатор клиента для сеанса сервера Oracle, выбирая идентификатор сеанса session_id из таблицы current_sessions. Затем, перед возвратом соединения с сервером базы данных в пул соединений, приложение может очистить этот идентификатор клиента. Это можно сделать следующим образом:
EXECUTE dbms_session.set_identifier ('session_id174837492748');
...выполнение запроса этого сеанса конечного пользователя...
EXECUTE dbms_session.clear_identifier
Теперь для трассировки этого сеанса конечного пользователя мы можем вызвать пакет dbms_monitor:
SQL> EXECUTE dbms_monitor.client_id_trace_enable -
>            ('session_id174837492748', waits=>TRUE, binds=>TRUE);
Этот вызов пакета dbms_monitor заставит каждый процесс сервера Oracle писать данные расширенной SQL-трассировки в трассировочный файл для всех операций, выполняемых от имени клиента с указанным значением идентификатора. Однако каждый процесс сервера Oracle пишет в свой собственный трассировочный файл. Поэтому данные трассировки будут разбросаны по разным файлам. Для решения этой проблемы в сервере Oracle 10g появилась новая утилита trcsess, работающая в режиме командной строки. Она читает разные трассировочные файлы и объединяет записи из различных файлов, удовлетворяющие заданным критериям, в одном трассировочном файле, который может быть обработан утилитой TKPROF. Чтобы объединить данные трассировки для нашего текущего примера, мы можем использовать следующие команды:
$ cd $ORACLE_BASE/admin/$ORACLE_SID/udump
$ trcsess output=/home/rschrag/case1403/case1403-trial1.trc \
          clientid=session_id174837492748
Таким образом, пакет dbms_monitor и утилита trcsess в сервере Oracle 10g намного упрощают сбор данных расширенной SQL-трассировки сеансов конечных пользователей, когда в звене сервера приложения используется пул соединений или другие способы агрегирования сеансов.
Заключение
В сервер Oracle 10g включено много усовершенствований интерфейса событий ожидания, которые как никогда раньше упрощают оптимизацию производительности с помощью методики событий ожидания. Некоторые усовершенствования, такие, как описательные имена событий ожидания, классы ожидания и процедура включения трассировки сеансов session_trace_enable в пакете dbms_monitor, облегчают нашу работу. Заметим, они не дают нам никакой информации, которую мы не имели прежде. Однако другие усовершенствования, такие, как статистические данные модели времени, представление v$event_histogram и новые столбцы в представлениях v$sql и v$sqlarea, предоставляют нам полезную информацию о распределении затрат времени и ожиданиях, ранее недоступную.
Хотя на момент написания данной статьи эти новые возможности были документированы неполно, а обсуждение на сайте Metalink проводилось удивительно редко, многие из этих усовершенствований окажутся, вероятно, весьма полезными для администраторов баз данных Oracle, ответственных за оптимизацию производительности сложных систем баз данных Oracle.