Ядро Oracle также выводит сведения о фактической продолжительности событий ожидания, которые имеют место между вызовами базы данных. К примерам событий ожиданий, происходящих между вызовами базы данных, относятся:
SQL*Net message from client SQL*Net message to client single-task message pipe get
rdbms ipc message pmon timer smon timer
Фрагмент файла трассировки, приведенный в примере 5.4, иллюстрирует события ожидания, которые имеют место между вызовами базы данных. Рассматриваемое приложение содержит ошибку - слишком часто выполняется разбор, что ограничивает возможность масштабирования. Как видите, фрагмент содержит два последовательных вызова разбора (выделенных жирным шрифтом) абсолютно одинакового текста SQL. Строки WAIT (помеченные жирным курсивом) находятся между вызовами разбора - как в смысле их физического расположения в файле трассировки, так и потому, что продолжительности этих событий не учитываются во времени, истекшем с начала второго вызова. Это легко проверить - фактическая длительность, показанная во второй строке PARSE (e=0), слишком мала для того, чтобы вмещать в себя продолжительность события SQL*Net message from client (ela=3).
Т
е
перь, когда вы все это знаете, вам легче будет представить себе соотношение между величинами c, e и ela в рамках всего файла трассировки. Учитывая все вышесказанное, общее время отклика сеанса равно общей продолжительности вызовов базы данных плюс общая продолжительность интервалов времени между такими вызовами. Формально можно представить это отношение так:
Есть, однако, еще одна сложность: наличие рекурсивного SQL приводит к двойному учету времени.
Рекурсивный SQL - это SQL, соответствующий вызову базы данных, значение dep которого больше нуля. Вызов базы данных глубиной dep=n + 1 (n = 0, 1, 2, ...) можно рассматривать как потомка какого-то вызова базы данных со значением dep=n. Сеансы приложений регулярно порождают достаточно сложные данные трассировки для представления всего спектра отношений между командами SQL, выступающими друг по отношению к другу в роли предков, потомков и т. д. Любой файл трассировки SQL содержит достаточно информации для того, чтобы можно было точно определить отношения типа «родитель-потомок» между вызовами базы данных. Для того чтобы не учесть какую-нибудь характеристику дважды при вычислении времени отклика, надо понимать, как выявить рекурсивные отношения между вызовами базы данных.
Термин рекурсивный характеризует исполнение ядром Oracle вызовов базы данных в контексте других вызовов базы данных. Рекурсивность может быть вызвана: выполнением команд DDL; выполнением блоков PL/SQL, включающих в себя команды DML; вызовами базы данных, к которым приводят к запуску триггеров, а также разнообразными командами DML, требующими доступа к словарю данных. Любой вызов базы данных, способный выполнять другой вызов базы данных, может служить причиной возникновения рекурсивного SQL.
Фрагмент файла трассировки, приведенный в примере 5.5, иллюстрирует использование рекурсивного SQL. В этом фрагменте есть информация о новом курсоре, помеченном как #2, который сопоставлен следующему тексту SQL:
select text from view$ where rowid=:1
Такого текста не было в исходном тексте исследуемого приложения -он появился в результате разбора запроса из представления DBA_OBJECTS.
Пример5.5. Фрагмент файла трассировки, демонстрирующий рекурсивный SQL. Три операции курсора #2 глубиной dep=1 являются рекурсивными
Правило определения рекурсивного отношения между вызовами базы данных формулируется очень просто:
Вызов базы данных, имеющий глубину dep=n+1, является рекурсивным потомком первого следующего за ним в потоке данных трассировки SQL вызова базы данных, имеющего глубину dep=n.
Пример 5.6 поясняет это правило. Ядро Oracle может выдать данные трассировки для вызова базы данных только по завершении действий (например, ядро не может вычислить фактическую продолжительность вызова, пока он не завершен). Поэтому мы можем воспроизвести последовательность инструкций, приведших к формированию данных трассировки SQL, приведенных в примере 5.5. В частности, в этом примере все вызовы базы данных для запроса к VIEW$ являются рекурсивными потомками вызова разбора запроса к DBA_OBJECTS. Для того чтобы подчеркнуть рекурсивное отношение «родитель-потомок» между вызовами базы данных, в тексте примера 5.6 для процедур стека вызовов использованы различные уровни отступа от левого края.
Пример 5.6. Последовательность инструкций ядра Oracle, которая выдает данные трассировки SQL в порядке, приведенном в примере 5.5. Величина отступа выбрана пропорционально глубине вызова
Графическое представление отношения «родитель-потомок» между вызовами базы данных дано на рис. 5.2.
В версиях Oracle (по крайне мере, вплоть до Oracle9i Release 2) такие статистики вызовов базы данных, как c, e, p, cr и cu, включают в себя ресурсы, потребленные собственно вызовом базы данных и всем его рекурсивным потомством.
Рекурсивное потомство вызова базы данных состоит из всех рекурсивных потомков вызова базы данных, включая детей, внуков, правнуков и т. д.
На рис. 5.3 представлено такое отношение для вымышленного набора вызовов базы данных. Каждый узел графа (прямоугольник) - это вызов базы данных (например, PARSE, EXEC или FETCH). Направленная линия от некоторого узла A к узлу B означает, что вызов базы данных A является рекурсивным родителем (т. е. вызывающим) для вызова базы данных B. Строка cr=n внутри узла - это то значение, которое ядро Oracle передаст в файл трассировки для вызова базы данных. Значение crself - это количество чтений в согласованном режиме, выполненных самим вызовом базы данных, без учета значений для дочерних вызовов.
Рис. 5.3. Каждая из величин c, e, p, cr и cu для вызова базы данных включает в себя соответствующие значения для всего рекурсивного генеалогического дерева данного вызова
Ядро выдает в файл трассировки только те значения статистик, которые включают в себя соответствующие значения для потомства, но из них можно извлечь составляющие, исключающие учет работы потомства (величины, указанные внутри дочерних вершин). Например, если бы числа внутри узлов на рис. 5.3 отсутствовали, было бы несложно их проставить. Значение каждого узла - это просто значение статистики для данного узла за вычетом суммы значений статистик для прямых потомков данного узла. Значение узла глубины dep=k- это значение cr, указанное для данного вызова базы данных за вычетом суммы значений cr его потомков глубины dep=k + 1. Обобщая, можно сказать,
что количество ресурсов s, потребленных вызовом базы данных глубины dep=k, вычисляется следующим образом:
где si - это значение статистики из набора {c, e, p, cr, cu}, выведенное ядром Oracle в файл трассировки для рекурсивной глубины i.
Описанный метод нетрудно применить к реальным данным трассировки. Обратимся еще раз к вызовам базы данных из примера 5.5. На рис. 5.4 приведено значение фактической продолжительности, включающее статистики для потомства, для каждого вызова базы данных (обозначенное e) и вклад каждого вызова в фактическую продолжительность без учета статистик потомков (обозначенный eself).
Рис. 5.4. Графическое представление стека рекурсивных вызовов из примера 5.5
В табл. 5.6 собраны все статистики для всех вызовов базы данных из примера 5.5, не учитывающие влияние потомства данных вызовов. Например, вклад (без учета потомков) в фактическую продолжительность вызова базы данных PARSE #1 можно вычислить следующим образом:
Ресурсы, потребляемые™
|
c
|
e
|
p
|
cr
|
cu
|
EXEC #2, потомок
|
0
|
176
|
0
|
0
|
0
|
FETCH #2, потомок
|
0
|
89
|
0
|
2
|
0
|
PARSE #1 без учета его рекурсивного потомства
|
10000
|
14701
|
0
|
0
|
0
|
Теперь у нас достаточно информации для завершения формулы учета времени отклика. Избавившись от двойного учета рекурсивного SQL, получим:
То есть общее время отклика по файлу трассировки приблизительно равно сумме значений e для вызовов базы данных рекурсивной глубины 0 плюс сумма значений ela для событий ожидания, которые имеют место между вызовами базы данных. С другой стороны, общее время отклика для файла приблизительно равно сумме значений c вызовов базы данных глубины 0 плюс сумма всех значений ela для файла.
< Предыдущая | Следующая > |
---|