Аналіз результатів tkprof, Інші СУБД, Бази даних, статті

Ця стаття присвячена аналізу результатів роботи утиліти tkprof і виявленню причин проблем продуктивності, про які ці результати свідчать.

 

Аналіз результатів tkprof


Том!

Я намагаюся інтерпретувати наступний результат, і не розумію, в чому справа. У таблиці TRACE– Всього 220 рядків. Що ти думаєш з цього приводу?

********************************************************************************
select TRACE01.TYPE ,TRACE01.STATUS ,TRACE01.SEQUENCE into :b0,:b1,:b2
from
TRACE TRACE01 where TRACE01.ENGINE=NVL(RTRIM(:b3,” “),” “)
call count cpu elapsed disk query current rows
——- —— ——– ———- ———- ———- ———- ———-
Parse 4 0.01 0.00 0 0 0 0
Execute 19012 3.10 3.56 0 0 0 0
Fetch 19012 1056.38 1517.15 0 43282981 76048 19012
——- —— ——– ———- ———- ———- ———- ———-
total 38028 1059.49 1520.71 0 43282981 76048 19012
Misses in library cache during parse: 0
Optimizer goal: CHOOSE
Parsing user id: 33 (OPS$MAXBATCH)
Rows Row Source Operation
——- —————————————————
4905 TABLE ACCESS FULL TRACE
Rows Execution Plan
——- —————————————————
0 SELECT STATEMENT GOAL: CHOOSE
4905 TABLE ACCESS GOAL: ANALYZED (FULL) OF “TRACE”
********************************************************************************

Відповідь Тома Кайта


Мітка максимального рівня (high water mark – HWM) (Максимальний розмір таблиці за весь час існування) дуже велика (близько 2275 блоків). Одного разу в таблиці було набагато більше 220 рядків.


При повному перегляді (full scan) таблиця проглядається аж до HWM. Розглянемо приклад:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t pctfree 90 pctused 10
2 as
3 select * from all_objects;
Table created.
ops$tkyte@ORA817DEV.US.ORACLE.COM> delete from t where object_id <> 55;
23663 rows deleted.
ops$tkyte@ORA817DEV.US.ORACLE.COM> commit;
Commit complete.
ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace on
ops$tkyte@ORA817DEV.US.ORACLE.COM> select object_name from t;
OBJECT_NAME
——————————
I_UGROUP1
Execution Plan
———————————————————-
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (FULL) OF “T”
Statistics
———————————————————-
0 recursive calls
27 db block gets
5395 consistent gets
0 physical reads
130980 redo size
377 bytes sent via SQL*Net to client
425 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace off

Бачите – 5395 consistent gets – Тому що таблиця була ось аткой великий. Для зменшення значення HWM треба або видалити (drop) таблицю, або усікти (truncate), і заново заповнити її рядками. В вашому випадку, однак, індекс по стовпцю TYPE теж “вирішить” проблему.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table tmp as select * from t;
Table created.
ops$tkyte@ORA817DEV.US.ORACLE.COM> truncate table t;
Table truncated.
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t select * from tmp;
1 row created.
ops$tkyte@ORA817DEV.US.ORACLE.COM> drop table tmp;
Table dropped.
ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace on
ops$tkyte@ORA817DEV.US.ORACLE.COM> select object_name from t;
OBJECT_NAME
——————————
I_UGROUP1
Execution Plan
———————————————————-
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (FULL) OF “T”
Statistics
———————————————————-
0 recursive calls
4 db block gets
1 consistent gets
0 physical reads
0 redo size
377 bytes sent via SQL*Net to client
425 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace off

Як бачите, в результаті ми отримуємо 1 consistent get


Як скинути HWM?


Том!

У мене є таблиця, що збільшується в розмірах зі, скажімо, 200 рядків вранці до, приблизно, 11000 рядків ввечері, коли пакетне завдання видаляє з неї рядки. Оскільки ця таблиця зберігає дані сеансів, вона дуже інтенсивно використовується. Який підхід слід застосовувати, щоб гарантіроват актуальності статистичної інформації протягом дня і скидати HWM вечорами, щоб вартісної оптимізатор не вів себе так, як було продемонстровано в твоєму прикладі? Чи можна скинути значення HWM, не втрачаючи доступу до даних?


У твоєму прикладі, якщо Проаналізовано таблицю t після видалення, що це дасть?


Відповідь Тома Кайта


Я продемонстрував два способи зменшити значення HWM для простої таблиці: truncate і drop.


Для зниження значення HWM використовуйте проміжну таблицю +truncate+insert.


Мабуть, у вас таблиця проглядається повністю кожен раз, індекс немає, так що, актуальність статистичної інформації, ймовірно, не має значення. Але якщо індекси є – аналізуйте таблицю періодично протягом дня. При 11000 рядків для цього буде потрібно кілька секунд.


Щодо останнього питання, см. це обговорення


Alter table move


Привіт, Том!

Замість використання проміжної таблиці +truncate+insert для зниження значення HWM я віддаю перевагу використовувати “alter table t move“. Потрібен лише один оператор і гарантується відсутність конфліктів з будь-якими обмеженнями цілісності, індексами тригерами і т.п.


Звідки 2275 блоків?


Том, як ти визначив, що в таблиці 2275 блоків?


Відповідь Тома Кайта

ops$tkyte@ORA920> select 43282981/19012 from dual;
43282981/19012
————–
2276.61377

query mode gets ділиться на кількість виконань запиту.


Уточнення


query mode gets ділиться на кількість виконань запиту.

Не дає ця формула кількість блоків, які обирають при кожному виконанні? Як це може дати нам загальна кількість блоків в таблиці?


І ще, чому кількість рядків, яке повертає tkprof (19012) відрізняється від зазначеного в плані виконання (4905)?


Відповідь Тома Кайта

Rows     Row Source Operation
——- —————————————————
4905 TABLE ACCESS FULL TRACE

TABLE ACCESS FULL – Значить, при кожному виконанні був прочитаний кожен блок. Беремо загальну кількість блоків, прочитаних при багаторазовому виконанні запиту, та ділимо на кількість виконань. Отримуємо кількість блоків у таблиці …


Кількість рядків не відрізняється – значення 4905 не є частиною плану виконання запиту. Це – кількість рядків, що пройшли через відповідну стадію плану виконання при реальному виконанні (іншими словами, це дійсне значення, так воно і було).


Далі, не варто сподіватися, що СТВЕРДЖУВАНЕ кількість рядків (ESTIMATED cardinality) завжди буде збігатися або онемного тлічаться від реального. Це ж припущення.


Спасибо … Коментар читача від 17 грудня 2002


Том,

Розрахунок кількості блоків в таблиці тепер мені набагато зрозуміліше, спасибо.


Що стосується кількості рядків: у вихідному питанні сказано, що в таблиці близько 220 рядків. Строк через стадію TABLE ACCESS FULL пройшло 4905 (> 220). За результатами tkprof, За 19012 виконань було опрацьовано 19012 рядків. Не міг би ти пояснити, як все це співвідноситься?


Відповідь Тома Кайта


Добре, знадобилося трохи подумати, але от їх ситуація, відтворена з точністю до деталей:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t (x int, y char(2000)) pctfree 90 pctused 10;
Table created.
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t select rownum, rownum from all_objects where rownum < 2275;
2274 rows created.
ops$tkyte@ORA817DEV.US.ORACLE.COM> commit;
Commit complete.
ops$tkyte@ORA817DEV.US.ORACLE.COM> delete from t where x > 220;
2054 rows deleted.
ops$tkyte@ORA817DEV.US.ORACLE.COM> commit;
Commit complete.
ops$tkyte@ORA817DEV.US.ORACLE.COM> exec show_space( “T” )
Free Blocks………………………..2054
Total Blocks……………………….2304
Total Bytes………………………..18874368
Unused Blocks………………………29
Unused Bytes……………………….237568
Last Used Ext FileId………………..7
Last Used Ext BlockId……………….38793
Last Used Block…………………….35
PL/SQL procedure successfully completed.

Отже, у нас є така ж таблиця – така ж кількість блоків і рядків. Тепер симітіруем їх процес.


Пам’ятайте – у них було 4 аналізу, так що я теж організовую 4 аналізу:

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter session set sql_trace=true;
Session altered.
ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
2 l_rec t%rowtype;
3 begin
4 for i in 1 .. 19012
5 loop
6 begin
7 if ( mod(i,4) = 0 )
8 then
9 select * into l_rec from t where x = mod(i,220)+1;
10 elsif ( mod(i,4) = 1 )
11 then
12 select * into l_rec from t where x = mod(i,220)+1;
13 elsif ( mod(i,4) = 2 )
14 then
15 select * into l_rec from t where x = mod(i,220)+1;
16 else
17 select * into l_rec from t where x = mod(i,220)+1;
18 end if;
19 exception
20 when no_data_found then null;
21 end;
22 end loop;
23 end;
24 /
PL/SQL procedure successfully completed.

Тепер викличемо утиліту tkprof зі стандартними установками (зокрема, aggregate=YES, Так що буде один загальний звіт для представлених вище 4 запитів – текст у них однаковий, так що, має сенс їх об’єднати:

SELECT *
FROM
T WHERE X = MOD (:b1,220) + 1
call count cpu elapsed disk query current rows
——- —— ——– ———- ———- ———- ———- ———-
Parse 4 0.00 0.00 0 0 0 0
Execute 19012 2.05 1.95 0 0 0 0
Fetch 19012 536.14 537.00 0 43234322 133084 19012
——- —— ——– ———- ———- ———- ———- ———-
total 38028 538.19 538.95 0 43234322 133084 19012
Misses in library cache during parse: 1
Optimizer goal: CHOOSE
Parsing user id: 605 (recursive depth: 1)
Rows Row Source Operation
——- —————————————————
4753 TABLE ACCESS FULL T

Знайомі результати? Приблизно така ж кількість query mode gets (Постійний повний перегляд 2275 блоків) …


Така ж кількість оброблених рядків, 19012.


У плані виконання запиту – 4753 рядки. Чому саме стільки, а не одне з наступних значень:

220 * 19 012 або
19012 ???

Щоб зрозуміти це, треба розібратися, що це взагалі за число. 4735 – це кількість рядків НА ВИХОДІ повного перегляду (тому по 220 рядків за кожний повний перегляд бути не може, оскільки запит повертає при кожному перегляді всього один рядок. Як максимум, в даному випадку можна очікувати 19012 рядків).


Ну, так і чому 4735? Це наслідок того, що запит аналізувався 4 рази (для нього в певний момент було 4 курсора, як я явно і зробив) І використовувався стандартний параметр aggregate=yes в командному рядку tkprof.


Aggregate = yes призводить до агрегації всіх записів EXEC (Зі значеннями consistent gets, cpu time і т.д.), але при цьому не можуть і не повинні агрегуватися записи STAT. Записи STAT – Це фактичні плани виконання запитів з кількістю рядків на виході відповідного кроку (врахуйте: у версіях аж до 8.0 видавалося кількість рядків НА ВХОДІ кожній стадії). Є 4 набору записів stat в результатах цього виклику tkprof, Оскільки було чотири запиту. Ви бачите записи stat тільки ДЛЯ ОДНОГО ІЗ НИХ. При виклику tkprof з параметром aggregate = no, Я отримав:

SELECT *
FROM
T WHERE X = MOD (:b1,220) + 1
call count cpu elapsed disk query current rows
——- —— ——– ———- ———- ———- ———- ———-
Parse 1 0.00 0.00 0 0 0 0
Execute 4753 0.56 0.36 0 0 0 0
Fetch 4753 134.05 134.48 0 10809356 33271 4753
——- —— ——– ———- ———- ———- ———- ———-
total 9507 134.61 134.84 0 10809356 33271 4753
Misses in library cache during parse: 1
Optimizer goal: CHOOSE
Parsing user id: 605 (recursive depth: 1)
Rows Row Source Operation
——- —————————————————
4753 TABLE ACCESS FULL T
——————————————————————————–
SELECT *
FROM
T WHERE X = MOD (:b1,220) + 1
call count cpu elapsed disk query current rows
——- —— ——– ———- ———- ———- ———- ———-
Parse 1 0.00 0.00 0 0 0 0
Execute 4753 0.60 0.62 0 0 0 0
Fetch 4753 133.89 134.01 0 10808322 33271 4753
——- —— ——– ———- ———- ———- ———- ———-
total 9507 134.49 134.63 0 10808322 33271 4753
Misses in library cache during parse: 0
Optimizer goal: CHOOSE
Parsing user id: 605 (recursive depth: 1)
Rows Row Source Operation
——- —————————————————
4753 TABLE ACCESS FULL T
SELECT *
FROM
T WHERE X = MOD (:b1,220) + 1
call count cpu elapsed disk query current rows
——- —— ——– ———- ———- ———- ———- ———-
Parse 1 0.00 0.00 0 0 0 0
Execute 4753 0.42 0.44 0 0 0 0
Fetch 4753 134.19 134.15 0 10808322 33271 4753
——- —— ——– ———- ———- ———- ———- ———-
total 9507 134.61 134.59 0 10808322 33271 4753
Misses in library cache during parse: 0
Optimizer goal: CHOOSE
Parsing user id: 605 (recursive depth: 1)
Rows Row Source Operation
——- —————————————————
4753 TABLE ACCESS FULL T
——————————————————————————–
SELECT *
FROM
T WHERE X = MOD (:b1,220) + 1
call count cpu elapsed disk query current rows
——- —— ——– ———- ———- ———- ———- ———-
Parse 1 0.00 0.00 0 0 0 0
Execute 4753 0.47 0.53 0 0 0 0
Fetch 4753 134.01 134.36 0 10808322 33271 4753
——- —— ——– ———- ———- ———- ———- ———-
total 9507 134.48 134.89 0 10808322 33271 4753
Misses in library cache during parse: 0
Optimizer goal: CHOOSE
Parsing user id: 605 (recursive depth: 1)
Rows Row Source Operation
——- —————————————————
4753 TABLE ACCESS FULL T

Бачите – 4 плану, 4 запити, і 4 записи stat, Кожна з 4753 рядками в плані запиту І в стовпці rows. Всі їх треба складати, але при “агрегування” утилітою tkprof вони не складаються.


Утиліта tkprof показує, що parses = execute. Що робити?


У мене parses=execute=4, fetch= 8 при єдиною обраній рядку. consistent gets=1.


Всі інші результати tkprof, Начебто, в порядку.

SELECT ROWID,UNIT_CODE, NAME, DEPT, ADDR_1, ADDR, LOCATION, STATE,
POSTAL_CODE, COUNTRY, COMPANY_DOCTOR, DOCTOR_ROLE, CONTACT_PHONE_NO,
CONTACT_FAX_NO, FDA_REGISTRATION_NUMBER, DATABASE_ID, TYPE_COMPANY_UNIT,
USER_ID, TO_CHAR(DATE_MODIFIED,”YYYY-MM-DD HH24:MI:SS”),
TO_CHAR(DATE_CREATED,”YYYY-MM-DD HH24:MI:SS”), RECORD_ID, UNIT_TYPE_FLAG,
DGFPS_NO, INTERCHANGE_ID, E2B_FILE_FORMAT
FROM
COMPANY_UNIT Where UNIT_CODE = “BIO”
call count cpu elapsed disk query current rows
——- —— ——– ———- ———- ———- ———- ———-
Parse 4 0.00 0.00 0 0 0 0
Execute 4 0.00 0.00 0 0 0 0
Fetch 8 0.00 0.00 0 8 0 4
——- —— ——– ———- ———- ———- ———- ———-
total 16 0.00 0.00 0 8 0 4
Misses in library cache during parse: 0
Optimizer goal: CHOOSE
Parsing user id: 43
Rows Row Source Operation
——- —————————————————
1 TABLE ACCESS BY INDEX ROWID COMPANY_UNIT
1 INDEX UNIQUE SCAN (object id 25233)

У книзі Expert one on one… ви рекомендуєте використовувати зв’язуються перменная, але тут мені потрібна константа.


В іншому тесті я отримав parse=execute=fetch= 159 при тій же кількості оброблених рядків. Як це зрозуміти?


Відповідь Тома Кайта


Вам треба знайти програміста, який написав цей код, і навчити його правилом “аналізувати один раз, виконувати – багато разів, курсор без крайньої потреби не закривати”


Це допоможе виправити кількість parse – Воно має дорівнювати 1. Кількість fetch теж має дорівнювати 4 – код у них написаний приблизно так:

   prepare
open
fetch
fetch
close

а треба:

 if (первий_раз) prepare
open
fetch using array fetch size of 2

Те ж і в більш “кричуще” прикладі з кількістю 159. Це – помилка програмування, яку допустили розробники. Вони занадто рано закривають курсор.


Row source 0


Том!

Я впевнений, що ти писав десь про те, чому значення “rows“В результатах роботи tkprof іноді 0. Не міг би ти дати посилання на цю статтю – мені не вдається її знайти …


Відповідь Тома Кайта


Я особисто такої статті не пам’ятаю … Якщо в результатах tkprof є план виконання і параметр explain = користувач / пароль не задавався, значить, записи STAT були.


Можливо, ви використовуєте параметр explain = користувач / пароль для генерації плану? Наприклад, якщо я зроблю так:

SQL> alter session set sql_trace=true;
SQL> select * from emp; SQL> host <тепер запустити tkprof з параметром explain=користувач / пароль>

Я отримую:

select * from  emp
call count cpu elapsed disk query current rows
——- —— ——– ———- ———- ———- ———- ———-
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 2 0.00 0.00 0 4 0 14
——- —— ——– ———- ———- ———- ———- ———-
total 4 0.00 0.00 0 4 0 14
Misses in library cache during parse: 0
Optimizer goal: CHOOSE
Parsing user id: 315 (OPS$TKYTE)
Rows Execution Plan
——- —————————————————
0 SELECT STATEMENT GOAL: CHOOSE
0 TABLE ACCESS (FULL) OF “EMP”

а якщо потім вийти і повернутися в sqlplus:

$ exit
SQL> exit

а потім вийти і з sqlplus (Закриваючи файл трасування), і знову запустити tkprof, Отримаємо наступний результат:

select * from  emp
call count cpu elapsed disk query current rows
——- —— ——– ———- ———- ———- ———- ———-
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 2 0.00 0.00 0 4 0 14
——- —— ——– ———- ———- ———- ———- ———-
total 4 0.00 0.00 0 4 0 14
Misses in library cache during parse: 0
Optimizer goal: CHOOSE
Parsing user id: 315 (OPS$TKYTE)
Rows Row Source Operation
——- —————————————————
14 TABLE ACCESS FULL OBJ#(48637) (cr=4 r=0 w=0 time=226 us)
Rows Execution Plan
——- —————————————————
0 SELECT STATEMENT GOAL: CHOOSE
14 TABLE ACCESS (FULL) OF “EMP”

Так що, може бути, ви бачите план виконання з нулями, і це просто означає “всі курсори були не закриті АБО файл трасування був усічений – досягнуто значення max dump size” …


Consistent gets?


Шановний Том!

На твою відповіді я зрозумів, що великі значення consistent gets – Це не дуже добре, і причиною може бути дуже велике значення HWM або великий розмір таблиці. Для вирішення цієї проблеми використовується або оператор alter table … move, Або експорт / імпорт.


Мене цікавить, як дізнатися, що у таблиці дуже велике значення HWM, і яким має бути ставлення значення consistent gets до загальної кількості рядків або до якогось іншого параметру?


Відповідь Тома Кайта


Очер РІДКО велику кількість consistent gets буває пов’язано з тим, що таблиця проглядається до (занадто великого) HWM.


У мільйон разів частіше це пов’язано з наступними причинами:



Не прив’язуйтесь до однієї причини, не завжди проблема пов’язана з HWM – фактично, вона з цим значення майже ніколи не пов’язана.


Постарайтеся виявити запити, найбільш істотно завантажили систему (за допомогою statspack). Сконцентруйтеся на зменшенні колічства операцій логічного введення / виводу, виконуваних ними, а для цього:



Ще проблеми з tkprof


Як визначити, що процес не використовує обробку масивів (array processing)?


Якщо для запиту я отримую:

call     count       cpu    elapsed       disk      query    current        rows
——- —— ——– ———- ———- ———- ———- ———-
Parse 0 0.00 0.00 0 0 0 0
Execute 3377 1.19 1.90 0 0 0 3377
Fetch 3377 1.82 342.39 4759 15243 0 2
——- —— ——– ———- ———- ———- ———- ———-
total 6754 3.01 344.29 4759 15243 0 3379

чи використовується в ньому обробка масивів?


І ще, звідки на стадії execute беруться читання з диска / читання буферів?


Відповідь Тома Кайта


Я сумніваюся. Але якщо і використовувалася, що з того? Обрано було всього два рядки.


У загальному випадку, поділіть кількість рядків на кількість операцій вибірки. Ви і отримаєте розмір масиву.


У вашому випадку, схоже, хтось виконує неефективний пошук.


Запити можуть включати стадію execute, Залежно від суті запиту, але ви ж не представили запит, так що, я можу тільки припускати 😉


Нечасто, проте, буває, що rows має ненульове значення і на стадії execute, І на стадії fetch. Було б цікаво побачити тестовий приклад, на якому цей результат отриманий.


Велика кількість прочитаних буферів при вставці


Хотілося б дізнатися, що може привести до великої кількості buffer gets при вставці даних. Я отримав такий результат tkprof:

INSERT INTO gcprt_scon_solic_cons
(scon_num_solic, dcrp_grp_cons, dcrp_tipo_cons, scon_tipo_movi,
scon_valo_cons, scon_param_cons, scon_estado_solic,
scon_usuario, scon_timestamp)
VALUES (gcprt_scon_num_seq.NEXTVAL, :b1, :b2, :b3,
:b4, :b5, :b6,
:b7, SYSDATE)
call count cpu elapsed disk query current rows
——- —— ——– ———- ———- ———- ———- ———-
Parse 1 0.02 0.02 0 0 0 0
Execute 530 11.40 69.05 7520 57698 6340 530
Fetch 0 0.00 0.00 0 0 0 0
——- —— ——– ———- ———- ———- ———- ———-
total 531 11.42 69.07 7520 57698 6340 530

У таблиці є всього один індекс (первинний ключ, одностолбцовий), значення current gets невелика (так що, я припускаю, в таблиці виконано не так вже й багато змін), але buffer gets становить близько 110 на кожне виконання. Мені здається, для вставки забагато. У чому може бути причина?


Відповідь Тома Кайта


Ну, так покажіть оператор create table та інші деталі – надто вже багато читань з ДИСКА (фізичного введення / виводу) та current mode gets для таблиці з єдиним індексом.


Визначення таблиці


Таблиця створюється так:

CREATE TABLE GCPRT_SCON_SOLIC_CONS
(
SCON_NUM_SOLIC NUMBER NOT NULL,
DCRP_GRP_CONS VARCHAR2(10) NOT NULL,
DCRP_TIPO_CONS VARCHAR2(10),
SCON_TIPO_MOVI VARCHAR2(2) NOT NULL,
SCON_VALO_CONS VARCHAR2(300) NOT NULL,
SCON_PARAM_CONS VARCHAR2(500),
SCON_ESTADO_SOLIC VARCHAR2(2) NOT NULL,
SCON_USUARIO VARCHAR2(8) NOT NULL,
SCON_TIMESTAMP DATE NOT NULL
)
TABLESPACE ETGCPRT100
PCTUSED 85
PCTFREE 10
INITRANS 1
MAXTRANS 255
STORAGE (
INITIAL 10M
NEXT 128M
MINEXTENTS 1
MAXEXTENTS 2147483645
PCTINCREASE 0
FREELISTS 1
FREELIST GROUPS 1
)
NOCACHE
NOPARALLEL;
ALTER TABLE GCPRT_SCON_SOLIC_CONS ADD (
CONSTRAINT PK_GCPRT_SCON_SOLIC_CONS PRIMARY KEY (SCON_NUM_SOLIC)
USING INDEX
TABLESPACE EIGCPRT100
PCTFREE 10
INITRANS 2
MAXTRANS 255
STORAGE (
INITIAL 10M
NEXT 10M
MINEXTENTS 1
MAXEXTENTS 2147483645
PCTINCREASE 0
FREELISTS 1
FREELIST GROUPS 1
));

Ще в неї є тригер по стовпцю SCON_ESTADO_SOLIC, – Він не може бути причиною? (Чи є значення buffer gets, Що видається tkprof, Сумарним для оператора insert і тригера?)


Відповідь Тома Кайта


Так, тригер і є причиною. Розглянемо приклад:

ops$tkyte@ORA817DEV> create table t ( x int primary key, y char(80) );
Table created.
ops$tkyte@ORA817DEV> @trace
ops$tkyte@ORA817DEV> alter session set events “10046 trace name context forever, level 12”;
Session altered.
ops$tkyte@ORA817DEV> insert into t /* test1 */ (x,y) values ( -1, “x” );
1 row created.
ops$tkyte@ORA817DEV> create or replace trigger t_trigger
2 before insert on t
3 declare
4 l_cnt number;
5 begin
6 select count(*) into l_cnt from all_objects;
7 end;
8 /
Trigger created.
ops$tkyte@ORA817DEV> insert into t /* test2 */ (x,y) values ( 1, “x” );
1 row created.
insert into t /* test1 */ (x,y)
values
( -1, “x” )
call count cpu elapsed disk query current rows
——- —— ——– ———- ———- ———- ———- ———-
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 1 10 1
Fetch 0 0.00 0.00 0 0 0 0
——- —— ——– ———- ———- ———- ———- ———-
total 2 0.00 0.00 0 1 10 1
insert into t /* test2 */ (x,y)
values
( 1, “x” )
call count cpu elapsed disk query current rows
——- —— ——– ———- ———- ———- ———- ———-
Parse 1 0.01 0.01 0 0 0 0
Execute 1 2.76 6.16 370 64886 9 1
Fetch 0 0.00 0.00 0 0 0 0
——- —— ——– ———- ———- ———- ———- ———-
total 2 2.77 6.17 370 64886 9 1

Результат tkprof у прикладі на стор 561 – “Oracle для професіоналів”


Мова йде про стр. 464 оригіналу або про стр. 561 першого тому мого перекладу. – Прим. В.К.


Привіт, Том!

Коли я спробував виконати зазначений приклад з книги (насправді, весь PL / SQL-блок після виконання команди ALTER SESSION SET EVENTS), То отримав наступний результат:

UPDATE emp set ename = lower(ename)
where
empno = :b1
call count cpu elapsed disk query current rows
——- —— ——– ———- ———- ———- ———- ———-
Parse 1 0.07 0.06 0 0 0 0
Execute 1 0.00 100.18 0 1 2 1
Fetch 0 0.00 0.00 0 0 0 0
——- —— ——– ———- ———- ———- ———- ———-
total 2 0.07 100.25 0 1 2 1
Misses in library cache during parse: 1
Optimizer goal: CHOOSE
Parsing user id: 88 (recursive depth: 1)
Rows Row Source Operation
——- —————————————————
1 UPDATE (cr=1 r=0 w=0 time=49141 us)
1 INDEX UNIQUE SCAN PK_EMP (cr=1 r=0 w=0 time=27 us)(object id 37436)
Elapsed times include waiting on following events:
Event waited on Times Max. Wait Total Waited
—————————————- Waited ———- ————
enqueue 33 3.12 100.13

Видана також інформація про очікування (wait events). Я думаю, що це нова властивість утиліти TKPROF в Oracle9i (9.2.0.3). Чи означає це, що вивчати вихідні трассіровочние файли більше не доведеться?


У представлених результатах видано очікування події “enqueue“І загальний час цього очікування. Мене цікавить стовпець”Times Waited“. Чому в цьому стовпці зазначено, що чекати довелося 33 рази? Звідки взялося це число?


Відповідь Тома Кайта


Так, це нова чудова можливість tkprof в Oracle9i “s (починаючи з версії 9.0).


Вивчати трасувань файл доведеться, щоб з’ясувати, яке зачение мала мінлива :B1, А от очікування там, дійсно, можна вже й не шукати …


Ми перевіряємо стан очікування enqueue wait кожні три секунди, перевіряючи, “і що ж відбувається”, а потім знову засинаючи в очікуванні того, що блокуючий сеанс зафіксує або відкотить транзакцію.


А чи не можна змінити цей інтервал в 3 секунди?


Зовсім забув про значення пов’язують змінних у вихідному файлі трасування. Дякую, що нагадав. Але, в принципі, утиліта tkprof вільно могла б видавати і ці значення теж (вони ж є у вихідному файлі трасування). Мені цікаво, чому вона їх не вибирає?


У своїй відповіді ти написав:


Ми перевіряємо стан очікування enqueue wait кожні три секунди, перевіряючи, “і що ж відбувається” …

Чи є ці 3 секунди стандартним таймаут для очікування в? Чому саме 3 секунди? Я припускаю, що менше було б занадто мало, а більше – занадто довго для чекає сеансу. Правильно?


1). Чи є цей таймаут 3 секунди стандартним для всіх версій Oracle або він різний для різних версій Oracle (або може змінитися в майбутньому)?


2). Чи залежить хоч якось це значення від конфігурації сервера?


3). Воно десь описано в документації або ми просто “його знаємо”?


Відповідь Тома Кайта


Утиліта tkprof значення пов’язують змінних поки не видає.


3 секунди вибрали розробники, – саме тому, наприклад, доводиться чекати до 3 секунд при виявленні взаємного блокування.


1) Це значення, як все взагалі, “може змінитися”.


2) Ні


3) Ми просто “його знаємо”. Точніше, це значення можна визначити за такими результатами, як представлені вище.


Рекурсивний SQL в tkprof


Том!

Не міг би ти прояснити, чи включає значення buffer gets, Що видається утилітою tkprof, Рекурсивний sql чи ні. У главі 10 керівництва Oracle 9i Performance Tuning Guide сказано:


“Ресурси, що видаються для оператора, включають весь SQL, виконаний по ходу обробки оператора. Тому включаються всі ресурси, використані в тригері, а також ресурси, що використовуються будь-якими рекурсивними SQL-операторами (наприклад, використовуваними при виділенні простору). Якщо включена трасування SQL Trace, TKPROF враховує ці ресурси двічі. Не намагайтеся налаштовувати продуктивність оператора DML, якщо фактично ресурс використаний на нижньому рівні рекурсії. ”

Однак я виконав простий тест:

select * from sto_tab
where id = sto_select.get_id(id)
call count cpu elapsed disk query current rows
——- —— ——– ———- ———- ———- ———- ———-
Parse 1 0.01 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 2 0.02 0.01 0 32 0 1
——- —— ——– ———- ———- ———- ———- ———-
total 4 0.03 0.02 0 32 0 1
Misses in library cache during parse: 1

У функції sto_select.get_id я виконую запит:

function get_id(x in number) return number is
xx number;
begin
select count(*) into xx from all_objects where rownum <= 100000;
return 1;
end;

Цей sql-оператор я бачу в файлі трасування:

SELECT count(*)
from
all_objects where rownum <= 100000
call count cpu elapsed disk query current rows
——- —— ——– ———- ———- ———- ———- ———-
Parse 1 0.02 0.01 0 0 0 0
Execute 2 0.00 0.00 0 0 0 0
Fetch 2 8.34 8.49 556 579806 0 2
——- —— ——– ———- ———- ———- ———- ———-
total 5 8.36 8.50 556 579806 0 2

Отже, значення buffer gets явно не було включено в початковий запит. Що, в керівництві Performance Tuning Guide помилка? А як щодо операторів DML? (Я виконував тест на сервері версії 9.2.0.3.)


Відповідь Тома Кайта


Це було правильно. Розглянемо приклад:

Connected to:
Oracle8i Enterprise Edition Release 8.1.7.4.0 – Production
With the Partitioning option
JServer Release 8.1.7.4.0 – Production

ops$tkyte@ORA817DEV> set echo on
ops$tkyte@ORA817DEV> @test
ops$tkyte@ORA817DEV> drop table t;

Table dropped.

ops$tkyte@ORA817DEV> create table t ( x int );

Table created.

ops$tkyte@ORA817DEV> create or replace trigger t_trigger
2 before insert on t
3 begin
4 for x in ( select count(*) from all_objects )
5 loop
6 null;
7 end loop;
8 end;
9 /

Trigger created.

ops$tkyte@ORA817DEV> @trace
ops$tkyte@ORA817DEV> alter session set events “10046 trace name context forever, level 12”;

Session altered.

ops$tkyte@ORA817DEV> insert into t values ( 1 );

1 row created.


tkprof видає:

insert into t
values
( 1 )

call count cpu elapsed disk query current rows
——- —— ——– ———- ———- ———- ———- ———-
Parse 1 0.00 0.00 0 0 0 0
Execute 1 2.54 5.83 359 64968 12 1
Fetch 0 0.00 0.00 0 0 0 0
——- —— ——– ———- ———- ———- ———- ———-
total 2 2.54 5.83 359 64968 12 1

Misses in library cache during parse: 1
Optimizer goal: CHOOSE
Parsing user id: 742
********************************************************************************

SELECT COUNT(*)
FROM
ALL_OBJECTS

call count cpu elapsed disk query current rows
——- —— ——– ———- ———- ———- ———- ———-
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 2 2.54 5.79 358 64967 4 1
——- —— ——– ———- ———- ———- ———- ———-
total 4 2.54 5.79 358 64967 4 1


Так що значення consistent gets раніше агрегувати. Але за тими ж даними в 9ir2 отримуємо:

insert into t
values
( 1 )

call count cpu elapsed disk query current rows
——- —— ——– ———- ———- ———- ———- ———-
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 1 20 1
Fetch 0 0.00 0.00 0 0 0 0
——- —— ——– ———- ———- ———- ———- ———-
total 2 0.00 0.00 0 1 20 1

Misses in library cache during parse: 1
Optimizer goal: CHOOSE
Parsing user id: 89

Elapsed times include waiting on following events:
Event waited on Times Max. Wait Total Waited
—————————————- Waited ———- ————
SQL*Net message to client 1 0.00 0.00
SQL*Net message from client 1 0.00 0.00
********************************************************************************

select count(*)
from
all_objects

call count cpu elapsed disk query current rows
——- —— ——– ———- ———- ———- ———- ———-
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 2 3.52 3.43 0 75729 0 1
——- —— ——– ———- ———- ———- ———- ———-
total 4 3.52 3.43 0 75729 0 1


так що він більше не враховує це двічі.


Я виставлю помилку в документації.


Кількість рядків, оброблених при виконанні insert


Привіт, Том!


Утиліта TKPROF видає:

INSERT INTO CDRCS_EO (col1, col2, col3, col4, col5)
VALUES
(:v1, :v2, :v3, :v4, :v5)
call count cpu elapsed disk query current rows
——- —— ——– ———- ———- ———- ———- ———-
Parse 291 1.25 14.77 0 0 0 0
Execute 291 15.40 1317.90 10011 1047 317037 105147
Fetch 0 0.00 0.00 0 0 0 0
——- —— ——– ———- ———- ———- ———- ———-
total 582 16.65 1332.67 10011 1047 317037 105147
Misses in library cache during parse: 0
Optimizer goal: RULE

Питання: Оператор був виконаний 291 раз. Це означає, що оброблена (вставлена) може бути максимум 291 рядок. Чому оброблено більше рядків, ніж кількість виконань оператора?


І ще, що цей оператор (insert) робить на диску?


Поясни, будь ласка.


Відповідь Тома Кайта


Розробник виконав чудову вставку масивом. Близько 370 рядків всатвляется при кожній вставці (шкода тільки, що курсор не залишили відкритим! В ідеалі повинен бути 1 аналіз, 291 виконання.)


Що стосується того, “що цей оператор (insert) робить на диску” – дані треба вставити “кудись”. При вставці сервер йде в список вільних місць; там сказано, що “у блоці 20 файла 5 є місце”. Якщо блоку 20 немає в буферному кеші, доведеться його прочитати з диска. Крім того, при вставці рядка в таблицю, по якій є індекси, дані повинні потрапити в цілком конкретні блоки. “name=frank“- Йде в блок 200 файлу 20 (це з’ясовується після прочитання кореневого і проміжних блоків індексу). Якщо блок 200 файлу 20 знаходиться на диску, його доведеться прочитати, щоб змінити.


Пакетне завдання: elapsed time. Коментар читача від 19 грудня 2003


У нас є пакетне завдання, яке виконується близько 5:00, але, протрассировать завдання, ми обробили файл трасування за допомогою tkprof і отримали наступне:

OVERALL TOTALS FOR ALL NON-RECURSIVE STATEMENTS
call count cpu elapsed disk query current rows
——- —— ——– ———- ———- ———- ———- ———-
Parse 595504 65.83 81.66 0 531 0 0
Execute 595508 346.99 452.31 5 25 21 8
Fetch 891520 1379.24 11759.50 1069624 6984725 3 605893
——- —— ——– ———- ———- ———- ———- ———-
total 2082532 1792.06 12293.48 1069629 6985281 24
605901
OVERALL TOTALS FOR ALL RECURSIVE STATEMENTS
call count cpu elapsed disk query current rows
——- —— ——– ———- ———- ———- ———- ———-
Parse 525 0.10 0.13 0 0 0 0
Execute 947 0.36 0.41 0 0 0 1
Fetch 1819 0.16 1.37 123 3457 0 1345
——- —— ——– ———- ———- ———- ———- ———-
total 3291 0.62 1.91 123 3457 0 1346

На думку TKPROF, Процес виконувався близько 3,9 години … Це що, загальний час виконання на сервері? Чи це взагалі не має відношення до часу виконання пакетного завдання сервером?


На що йде 1,1 години?


Відповідь Тома Кайта


Ці 1,1 години йдуть на передачу даних по мережі + час обробки на клієнті. Ймовірно, в основному на обробку на клієнта, з урахуванням середньої кількості повернених рядків (якщо тільки у вас не надто повільна мережа)


Ваш пакетний процес повинен аналізувати оператори один раз, а виконувати багаторазово. Те, що parse майже збігається з execute– Погано (розробникам треба це виправити).


Можливо, SOFT parse …


Ваш пакетний процес повинен аналізувати оператори один раз, а виконувати багаторазово. Те, що parse майже збігається з execute– Погано (розробникам треба це виправити).

У представленому звіті TKPROF більшість виконаних аналізів могли бути типу SOFT PARSE. Правильно? І як розробники можуть це виправити?


Відповідь Тома Кайта


Розробники повинні писати так:

підпрограма X static variable ОПЕРАТОР
if (оператор ще не був проаналізований в цій підпрограмі)
then проаналізувати оператор
end if;
bind оператор
execute оператор
/ * Ніколи не закривати його, залишити відкритим * / end підпрограма

Аналіз є аналіз. Швидше за все він буде виконуватися, якщо ВЗАГАЛІ не аналізувати.


Запитайте розробників: “Стали б ви Компілювати підпрограму перед кожним виконанням – виконувати код і тут же викидати”. Звичайно, не стали б. Так чому вони так поступають з SQL-операторами?


Але це ж web-додаток


/ * Ніколи не закривати його, залишити відкритим * /

Але в web-додатку дуже поасно НЕ ЗАКРИВАТИ оператор. Стан не зберігається, і транзакції – максимально короткі. Ми не знаємо, яким буде наступний запит.


Відповідь Тома Кайта


Можено тримати курсори відкритими як можна довше.


Якщо діяти інакше, ви різко знизите продуктивність.


Не бачу причин, чому, якщо ви аналізуєте і виконуєте оператор 500 разів на одній web-сторінці, НЕ МОЖНА проаналізувати його один раз і виконати 500 раз.


Немає причин (крім “мені ліньки”).


Один з варіантів – побудувати хеш-таблицю для підключення – кожен проаналізований / виконуваний оператор, який ви відкриваєте, потрапляє в цю хеш-таблицю. Перш ніж аналізувати будь-який оператор пошукайте для нього дескриптор в хеш-таблиці. Якщо ви його отримаєте, використовуйте повторно. Якщо ні – проаналізуйте (prepare) оператор і додайте його в таблицю. Потім, перш ніж повертати підключення НАЗАД в пул (я припускаю, що ви використовуєте пул підключень, що не гарантовано на 100%, але швидше за все, судячи з питання), треба пройти по записах хеш-таблиці і закрити всі оператори.


Знаєте, я використовую модуль mod_plsql, Який прекрасно “скидає стан сеансу” після кожного дзвінка. Ніяких витоків курсорів при переході зі сторінки на сторінку. Якщо використовувати більш складну мову, типу Java, доведеться займатися цим самостійно, але якщо НЕ займатися зовсім, ви отримаєте низьку продуктивність і обмежену масштабованість. Так що, в кінцевому рахунку, вирішувати вам.

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*