Pages

4 Mayıs 2011 Çarşamba

DBMS_SQLTUNE paketi ile SQL uygulamalarının analizi


Oracle 11g sürümünden itibaren, DBMS_SQLTUNE paketi içinden SELECT_SQL_TRACE fonksiyonu çalıştırılmaktadır. Bu fonsiyonun amacı; SQL izleme dosyasının içeriğini bir SQL iyileştirme seti içerisine yüklemektir. Bu yazımın amacı SELECT_SQL_TRACE fonksiyonunu kullanarak SQL komutlarının içeriğinin SQL izleme dosyasından görülebilmesinin kullanımını ve faydalarını göstermektir.

SELECT_SQL_TRACE özelliği Oracle 11.1.0.7 sürümünden itibaren işlemdedir, yani bu sürüm öncesi Oracle 11g sürümlerinde çalışmaz. Kısaca DBMS_SQLTUNE.SELECT_SQL_TRACE fonsiyonu ile kullanılan parametrelerin kısaca anlamlarına ve nasıl kullanıldığına gelirsek;

dbms_sqltune.select_sql_trace(
            directory => 'TRACE',
            file_name => 'aysun_ora_21351.trc',
            select_mode => 1 | 2 )

directory: SQL izleme dosyalarının yer aldığı fiziksel lokasyonu işaret eden sanal dizinin ismidir. CREATE DIRECTORY komutu ile oluşturulur.
file_name: SQL izleme setine yüklenecek olan SQL izleme dosyasının adı.
select_mode: 1 veya 2 integer değerinden birini alır. 1=> SINGLE_EXECUTION, yani tek bir SQL çalıştırmasını döndürür 2=>ALL_EXECUTIONS, yani tüm SQL çalıştırmalarını döndürür

İlk olarak senaryoyu belirleyelim.

  • Gerekli veritabanı objelerini oluşturup obje istatistiklerini topluyoruz. Bu amaçla 10,000 satırdan oluşan test1 adında bir tablo ile bu tablonun id kolonunu işaret eden primary key indeksini oluşturup, tablonun tüm satırlarını istatistiklemeye dahil etmek üzere tablo istatistiklerini topluyorum.
SQL> CREATE TABLE test1
  2  AS
  3  SELECT rownum AS id, dbms_value.string(‘U’,50) AS adres
  4  FROM dual
  5  CONNECT BY level <= 10000
  6  ORDER BY dbms_random.value;

SQL> ALTER TABLE test1 ADD CONSTRAINT test1_pk PRIMARY KEY (id);

SQL> BEGIN
  2    dbms_stats.gather_table_stats(
  3      ownname          => hr,
  4      tabname          => 'test1',
  5      estimate_percent => 100,
  6      method_opt       => 'for all columns size 1'
  7    );
  8  END;
  9  /

  • SQL izlemesini etkinleştiriyorum. Bu arada Oracle 11g itibariyle SQL izlemesinde devreye giren PLAN_STAT adlı parametreyide kullanıyorum. PLAN_STAT parametresi, satır kaynak istatistiklerinin hangi sıklıkta izleme dosyalarına yazıldığını belirtmeye yaramaktadır. Bu parametre 3 değer alabilmektedir. NEVER: çalıştırma planı hakkında hiç bir bilgi izleme dosyasına yazılmaz. FIRST_EXECUTION: Çalıştırma planı ile ilgili bilgi ilk çalıştırmadan sonra izleme dosyasına yazılır. ALL_EXECUTIONS: Her çalıştırma sonrasında çalıştırma planı ile ilgili bilgi izleme dosyasına yazılmaktadır. Bu önemli bir parameterdir, çünkü Oracle 10g’de bilhassa imleçleri uzun zaman açık tutan uygulamalar için,  her bir imleç için çalıştırma planları hakkında bilgiye izleme dosyalarında rastlamak mümkün değildi.
SQL> execute dbms_monitor.session_trace_enable(binds => TRUE, plan_stat => 'ALL_EXECUTIONS')

  • Sistemde bir takım sorgular oluşturuyoruz. Bilhassa imleç işlemleri ile bu basit sorguları çalıştırıyorum. 
SQL> EXECUTE :id := 5;

SQL> SELECT count(adres) FROM test1 WHERE id < :id;

COUNT(adres)
------------
         4

SQL> EXECUTE :id := 850;

SQL> SELECT count(adres) FROM test1 WHERE id < :id;

COUNT(adres)
----------
       849

SQL> SELECT count(adres) FROM test1 WHERE id < :id;

COUNT(adres)
----------
       849

SQL> EXECUTE :id := 35;

SQL> SELECT count(adres) FROM test1 WHERE id < :id;

COUNT(adres)
----------
        34

SQL> SELECT sum(id) FROM test1;

   SUM(ID)
----------
  50005000

SQL izlemesini devredışı bırakıyorum ve SQL izleme dosyasının adını alıyorum.

SQL> execute dbms_monitor.session_trace_disable

SQL> SELECT value
  2  FROM v$diag_info
  3  WHERE name = 'Default Trace File';

VALUE
--------------------------------------------------------------
/u00/app/oracle/diag/rdbms/aysun/aysun/trace/aysun_ora_21351.trc

  • SQL komutları arasından SQL dosyalarını okumak için bir dizin oluşturuyorum. 
SQL> CREATE DIRECTORY trace AS '/u00/app/oracle/diag/rdbms/aysun/aysun/trace/';

Şimdi artık bir SQL izleme dosyası mevcuttur. Şimdi bu dosyanın içeriğini bazı basit sorgular ile nasıl okuyabileceğimizi ve ne tür birgileri dışarı çıkarabileceğimizi göreceğiz.

Bu kullanıcı tarafından çalıştırılan SQL komutlarının listesini tamamlanma süreleri ve çalıştırma sayıları ile alıyorum ve SYS kullanıcısı tarafından çalıştırılan recursive SQL komutlarını hariç tutuyorum.

SQL> SELECT sql_id,
  2         sum(elapsed_time) AS elapsed_time,
  3         sum(executions) AS executions,
  4         round(sum(elapsed_time)/sum(executions)) AS elapsed_time_per_execution
  5  FROM table(dbms_sqltune.select_sql_trace(
  6               directory => 'TRACE',
  7               file_name => 'aysun_ora_21351.trc',
  8               select_mode => 2
  9            )) t
 10  WHERE parsing_schema_name = 'HR'
 11  GROUP BY sql_id
 12  ORDER BY elapsed_time DESC;


SQL_ID          ELAPSED_TIME EXECUTIONS ELAPSED_TIME_PER_EXECUTION
-------------   ------------ ---------- --------------------------
aot15tfsat23u   249757       4          62439
3tgnxpwym1dcq   4200         1          4200

Belirli bir SQL komutunun SQL textini alıyoruz.

SQL> SELECT sql_text
  2  FROM table(dbms_sqltune.select_sql_trace(
  3               directory => 'TRACE',
  4               file_name => 'aysun_ora_21351.trc',
  5               select_mode => 1
  6            )) t
  7  WHERE sql_id = 'aot15tfsat23u';


SQL_TEXT
---------------------------------------
SELECT count(adres) FROM t WHERE id < :id

Belirli bir SQL komutu hakkında daha fazla çalıştırma istatistiklerini alıyorum.

SQL> SELECT plan_hash_value, executions, fetches, elapsed_time, cpu_time, disk_reads, buffer_gets, rows_processed
  2  FROM table(dbms_sqltune.select_sql_trace(
  3               directory => 'TRACE',
  4               file_name => 'aysun_ora_21351.trc',
  5               select_mode => 2
  6            )) t
  7  WHERE sql_id = 'aot15tfsat23u'
  8  ORDER BY elapsed_time DESC;


PLAN_HASH_VALUE EXECUTIONS    FETCHES ELAPSED_TIME   CPU_TIME DISK_READS BUFFER_GETS ROWS_PROCESSED
--------------- ---------- ---------- ------------ ---------- ---------- ----------- --------------
     5294676952          1          2       129056     127981        731         992              1
     5294676952          1          2       113667     112982        691        1434              1
     5294676952          1          2         5993       6999         11          11              1
     5294676952          1          2         1041       1000          0          21              1

Bind değişkenlerinin değerlerini alıyorum.

SQL> SELECT elapsed_time,
  2         value(b).gettypename() AS type,
  3         value(b).accessnumber() AS value
  4  FROM table(dbms_sqltune.select_sql_trace(
  5               directory => 'TRACE',
  6               file_name => 'aysun_ora_21351.trc',
  7               select_mode => 2
  8            )) t,
  9       table(bind_list) b
 10  WHERE sql_id = 'aot15tfsat23u'
 11  ORDER BY elapsed_time DESC;


ELAPSED_TIME TYPE       VALUE
------------ ---------- -----
      120543 SYS.NUMBER   990
      112375 SYS.NUMBER   990
        5891 SYS.NUMBER    10
        1172 SYS.NUMBER    20

Herşeyin iyi gözükmesine rağmen DBMS_SQLTUNE.SELECT_SQL_TRACE kullanımı ile alınan sonuçta bir takım hatalarda yok değildir. Özellikle, çalıştırma planın hash değeri tüm çalıştırılan SQL komutları için aynı gözüktüğünden dolayı hash değerleri her zaman doğru listelenmeyebilir. Bunun sebebi Oracle 11g ile kullanılmaya başlanan “adaptive cursor sharing” özelliğindendir. Doğru hash değerleri, SQL izleme dosyalarına TKPROF ile bakıldığında ortaya çıkmaktadır. Aşağıda, TKPOF komutunun AGGREGATE parametresinin “NO” değeri ile çalıştırılması sonuçları yer almaktadır. Üçüncü çalıştırmanın(kırmızı olan) hash değerinin farklı olduğuna dikkat ediniz.

...
SQL ID: aot15tfsat23u
Plan Hash: 5170755809
SELECT count(adres) FROM t WHERE id < :id
...
SQL ID: aot15tfsat23u
Plan Hash: 5170755809
SELECT count(adres) FROM t WHERE id < :id
...
SQL ID: aot15tfsat23u
Plan Hash: 2966233522
SELECT count(pad) FROM t WHERE id < :id
...
SQL ID: aot15tfsat23u
Plan Hash: 5170755809
SELECT count(adres) FROM t WHERE id < :id
...

SELECT_SQL_TRACE fonksiyonu üzerinden alınabilecek diğer bir bilgi ise çalıştırma planıdır. Ne yazıkki, bunu direct olarak bu fonksiyon üzerinden ekstrakt etmek sakıncalıdır. Bunun sebebi format işleminin kendi başımıza yapılması gerekliliğidir. Daha kolayı ise, bir SQL iyileştirme seti oluşturarak bilginin bu SQL iyileştirme setine yüklenmesi ve ardından DBMS_XPLAN paketi ile bu SQL iyileştirme setinin içeriğinin gösterilmesidir. Aşağıdaki örnekte bu olay gösterilmektedir.

SQL> DECLARE
  2    c sys_refcursor;
  3  BEGIN
  4    dbms_sqltune.create_sqlset('TEST');
  5    OPEN c FOR
  6      SELECT value(t)
  7      FROM table(dbms_sqltune.select_sql_trace(
  8               directory => 'TRACE',
  9               file_name => 'aysun_ora_21351.trc',
 10               select_mode => 2
 11                )) t;
 12    dbms_sqltune.load_sqlset('TEST', c);
 13    CLOSE c;
 14  END;
 15  /

SQL> SELECT *
  2  FROM table(dbms_xplan.display_sqlset(
  3               sqlset_name => 'TEST',
  4               sql_id => 'aot15tfsat23u'
  5            ));

PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------
SQL Tuning Set Name: TEST
SQL Tuning Set Owner: HR
SQL_ID: aot15tfsat23u
SQL Text: SELECT count(adres) FROM t WHERE id < :id
-----------------------------------------------------------------------------

-----------------------------------------------------------------------------
| Id  | Operation                    | Name                 | Bytes | Cost  |
-----------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |                      |       |       |
|   1 |  SORT AGGREGATE              |                      |       |       |
|   2 |   TABLE ACCESS BY INDEX ROWID| UNKNOWN_OBJECT_90222 |  9045 |    11 |
|   3 |    INDEX RANGE SCAN          | UNKNOWN_OBJECT_90223 |       |     2 |
-----------------------------------------------------------------------------

Bu durumda sadece tek bir çalıştırma planı görülmektedir. Her ne kadar yeni ve iyi bir özellik olsada bekleme olaylarının görülmemesinden dolayı profiler aracının yerine geçmesi mümkün olmamaktadır, ancak bekleme olaylarının gözlenmesinin gerekmediği durumlarda, okuma kolaylığı ve komutların kolayca çalıştırılıp sonuçların düzgün formatta alınmasından dolayı kullanımı elverişli olacaktır.

0 yorum:

Yorum Gönder