Oracle SQL Patch: Koda Dokunmadan SQL Performansını Yönetmek

YUNUS EMRE ATAY

Gece yarısı gelen bir performans alarmını düşünün. Normalde bir saniyede dönen bir sorgu artık kırk saniye sürüyor. SQL metni değişmemiş, tablo yapısı aynı, uygulama tarafında yeni bir sürüm de yayınlanmamış — ama execution plan bambaşka. Üstüne bir de uygulama ekibi yeni sürümü ancak önümüzdeki hafta çıkarabileceğini söylüyor.

Böyle anlarda sorulması gereken doğru soru şu: kodu değiştirmeden Optimizer’ın kararını değiştirebilir miyim? Oracle Database’de bu sorunun cevabı SQL Patch.

SQL Patch Nedir?

SQL Patch, DBMS_SQLDIAG paketi üzerinden sunulan ve belirli bir SQL ifadesine dışarıdan Hint enjekte etmenizi sağlayan bir mekanizmadır. En kritik özelliği şudur: SQL metnini hiçbir şekilde değiştirmez. Uygulama aynı SQL’i göndermeye devam eder; Oracle Optimizer bu SQL’i derlerken (parse ederken) arka planda patch içindeki Hint’leri plana enjekte eder.

Bu özellik özellikle kaynak koduna erişiminizin olmadığı paket uygulamalarda (SAP, Oracle E-Business Suite, Logo gibi) hayat kurtarır — SQL metnine dokunma şansınız olmasa bile Optimizer’ın davranışını yönlendirebilirsiniz.

Uygulama → SELECT ... FROM ORDERS ...
                    │
                    ▼
            Oracle SQL Parser
                    │
                    ▼
      Bu SQL için tanımlı bir Patch var mı?
          │                         │
        Hayır                     Evet
          │                         │
          ▼                         ▼
   Normal plan üretilir     Patch'teki Hint'ler
                             plana enjekte edilir
                    │
                    ▼
             Execution Plan

Ne Zaman İşe Yarar?

  • Kaynak koduna erişemediğiniz paket uygulamalarda
  • Yeni bir release beklemeden acil bir performans müdahalesi gerektiğinde
  • Optimizer yanlış bir erişim yolu veya join sırası seçtiğinde
  • Yıllar önce koda gömülmüş, tablo büyüdükçe zararlı hale gelmiş bir Hint’i etkisiz kılmak gerektiğinde

Adım 1: SQL_ID ve Query Block Tespiti

Öncelikle sorunlu sorgunun SQL_ID değerini buluyoruz:

SELECT sql_id, sql_text
FROM   v$sql
WHERE  sql_text LIKE '%ORDERS%';

Ardından mevcut planı OUTLINE formatında görüntülüyoruz — bu format, sorgudaki her tablonun hangi query block’a ait olduğunu (@"SEL$1" gibi) gösterir:

SELECT * FROM TABLE(
  DBMS_XPLAN.DISPLAY_CURSOR('amz7zfdk33czb', NULL, 'ADVANCED +HINT_REPORT')
);

Query block isimlerini bilmek önemlidir, çünkü SQL Patch’e yazacağınız Hint’lerin Optimizer tarafından doğru tabloya ve doğru sorgu bloğuna uygulanmasını garanti eder. İlk bakışta karmaşık görünse de OUTLINE çıktısından kopyala-yapıştır yaparak kolayca doğru sözdizimine ulaşırsınız.

Adım 2: Patch Oluşturma

Temel sözdizimi şöyledir:

DECLARE
    l_patch VARCHAR2(100);
BEGIN
    l_patch := DBMS_SQLDIAG.CREATE_SQL_PATCH(
    sql_id    => 'amz7zfdk33czb',
    hint_text => q'[INDEX(@"SEL$1" "ORDERS"@"SEL$1" ORDERS_IDX1)]',
    name      => 'patch_orders_idx'
    );
END;
/

İki pratik not:

  • sql_id yerine sql_text parametresi de kullanılabilir. Sorgu shared pool’dan düşmüşse sql_id ile patch oluşturamazsınız; bu durumda sql_text kullanmak sizi bu bağımlılıktan kurtarır.
  • Prosedürü çalıştırmak için CREATE ANY SQL PATCH sistem yetkisine (kaldırmak için DROP ANY SQL PATCH‘e) sahip olmanız gerekir.


Senaryo 1: Sorguya Yeni Bir Hint Eklemek

Aşağıdaki sorguda ORDERS.CUSTOMER_ID üzerinde indeks bulunmasına rağmen Optimizer Full Table Scan yapıyor olsun:

SELECT * FROM orders WHERE customer_id = :b1;

Uygulama kodunu değiştirmeden ORDERS_IDX1 indeksini zorlamak için yukarıdaki patch yeterlidir. Bir sonraki parse’da sorgu otomatik olarak indeksi kullanır.


Senaryo 2: Koddaki Hatalı Hint’i Devre Dışı Bırakmak

Bir geliştiricinin yıllar önce sorguya eklediği /*+ FULL(t2) */ Hint’i, tablo büyüdükçe sistemi yavaşlatmaya başlamış olsun:

SELECT /*+ FULL(t2) */ SUM(t1.amount)
FROM   orders t1, customers t2
WHERE  t1.customer_id = t2.id;

Burada dikkat edilmesi gereken bir nokta var: patch’e sadece yeni bir Hint eklerseniz, bu Hint koddaki mevcut Hint’le birleşir, onun yerini almaz. Var olan bir Hint’i tamamen geçersiz kılmak için IGNORE_OPTIM_EMBEDDED_HINTS parametresini kullanmanız gerekir:

DECLARE
    l_patch VARCHAR2(100);
BEGIN
    l_patch := DBMS_SQLDIAG.CREATE_SQL_PATCH(
    sql_id    => 'amz7zfdk33czb',
    hint_text => 'IGNORE_OPTIM_EMBEDDED_HINTS',
    name      => 'patch_disable_full_hint'
    );
END;
/

Bu patch devreye girdiğinde, SQL içindeki tüm gömülü Hint’ler yok sayılır ve Optimizer kendi kararını verir.


Senaryo 3: Eski Hint’i Silip Yerine Yenisini Koymak

Genellikle en kullanışlı yöntem budur: koddaki hatalı Hint’i iptal edip aynı anda kendi Hint’inizi devreye almak.

DECLARE
    l_patch VARCHAR2(100);
BEGIN
    l_patch := DBMS_SQLDIAG.CREATE_SQL_PATCH(
    sql_id    => 'amz7zfdk33czb',
    hint_text => q'[
        IGNORE_OPTIM_EMBEDDED_HINTS
        LEADING(@"SEL$1" "T2"@"SEL$1" "T1"@"SEL$1")
        INDEX(@"SEL$1" "T2"@"SEL$1" CUST_IDX)
        ]',
        name      => 'patch_override_and_rehint'
    );
END;
/

Sonuç: koddaki FULL(t2) Hint’i devre dışı kalır, Optimizer yalnızca sizin verdiğiniz LEADING ve INDEX Hint’lerine göre plan üretir.


Bonus: OPT_PARAM ile Optimizer Parametresini Tek Sorguda Değiştirmek

SQL Patch, erişim yolu veya join sırası gibi klasik Hint’lerle sınırlı değil. OPT_PARAM Hint’i sayesinde, tüm sistemi etkileyecek bir spfile değişikliği yapmadan sadece tek bir sorgu için optimizer parametresini geçici olarak ayarlayabilirsiniz:

DECLARE
    l_patch VARCHAR2(100);
BEGIN
    l_patch := DBMS_SQLDIAG.CREATE_SQL_PATCH(
    sql_id    => 'amz7zfdk33czb',
    hint_text => q'[OPT_PARAM('optimizer_index_cost_adj' 20)]',
    name      => 'patch_orders_cost_adj'
    );
END;
/

Bu, özellikle tek bir sorgunun indeks kullanımına karşı “isteksiz” davrandığı ama global parametreyi değiştirmek istemediğiniz durumlarda çok işe yarar.


Patch’in Kullanıldığını Doğrulamak

Planı yeniden görüntülediğinizde, altındaki Note bölümü patch’in devrede olduğunu doğrular:

Note
-----
   - SQL patch "patch_override_and_rehint" used for this statement

Sistemdeki tüm aktif patch’leri listelemek için:

SELECT name, sql_text, status, created
FROM   dba_sql_patches;


Patch’i Kaldırmak

Kod düzeltildiğinde veya patch’e artık ihtiyaç kalmadığında:

BEGIN
    DBMS_SQLDIAG.DROP_SQL_PATCH(name => 'patch_override_and_rehint');
END;
/


SQL Patch, SQL Profile ve SQL Plan Baseline Farkı

Bu üç mekanizma sık sık karıştırılır ama amaçları farklıdır:

ÖzellikSQL PatchSQL ProfileSQL Plan Baseline
SQL metnini değiştirir miHayırHayırHayır
Manuel Hint eklenebilir miEvetHayırDolaylı yoldan
Belirli bir planı sabitler miHayırHayırEvet
Temel amacıOptimizer davranışını manuel yönlendirmekOptimizer’ın maliyet tahminini iyileştirmekBilinen iyi bir planı koruma altına almak

Kısacası: Optimizer’a “şunu yap” demek istiyorsanız SQL Patch, Optimizer’ın daha doğru tahmin yapmasına yardımcı olmak istiyorsanız SQL Profile, bilinen iyi bir planı sabitlemek istiyorsanız SQL Plan Baseline devreye girer.


Dikkat Edilmesi Gerekenler

  • Geçici bir müdahale aracıdır. Kalıcı çözüm her zaman istatistiklerin, indeks yapısının veya SQL’in kendisinin düzeltilmesidir.
  • Eşleştirme mantığını bilin. Patch, SQL metninin normalize edilmiş haline göre eşleşir; boşluk ve anahtar kelimelerin büyük/küçük yazımı sorun çıkarmaz. Ancak literal değerlerin değişmesi veya SQL’in yapısal olarak farklılaşması eşleşmeyi bozar.
  • Multitenant ortamda PDB bazlıdır. CDB$ROOT’ta oluşturduğunuz bir patch diğer PDB’leri etkilemez; her PDB kendi patch’lerini ayrı yönetir.
  • Yazım hataları sessizce yutulur. Yanlış yazılan bir Hint, hata vermeden yok sayılır. Bu yüzden Hint’i patch’e koymadan önce mutlaka OUTLINE çıktısıyla doğrulayın ve önce test ortamında deneyin.


Sonuç

SQL Patch, kriz anlarında yeni bir release beklemeden, paket uygulamanın iç yapısına dokunmadan Optimizer’ın davranışını değiştirmenizi sağlayan esnek bir araçtır. Özellikle IGNORE_OPTIM_EMBEDDED_HINTS ile koddaki hatalı Hint’leri geçersiz kılabilmek, veritabanı yöneticilerinin elindeki en pratik tekniklerden biridir — ama unutmayın, asıl hedef her zaman kök nedeni çözmek olmalı.


Sık Sorulan Sorular

SQL Patch SQL metnini değiştirir mi? Hayır. SQL metni aynı kalır; Oracle yalnızca parse aşamasında Optimizer’a ek Hint uygular.

Patch kalıcı mıdır? Evet. Siz DROP_SQL_PATCH ile kaldırana kadar veritabanında kalır.

RAC ortamında çalışır mı? Evet. Patch veritabanı seviyesinde saklandığı için tüm instance’lar tarafından kullanılır.

Her performans sorununu çözer mi? Hayır. Güncel olmayan istatistikler, eksik indeksler veya hatalı tasarım gibi kök nedenler varsa önce onların giderilmesi gerekir. SQL Patch, doğru analiz sonrasında uygulanan hedefe yönelik bir müdahaledir.


Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Hey!

Merhaba! Ben EMRE, teknoloji, yazılım veri tabanı ve veri analitiği alanlarına tutkuluyum. Bu blogda, öğrendiklerimi ve tecrübelerimi paylaşarak faydalı içerikler sunmayı hedefliyorum. Boş zamanlarımda yeni teknolojiler keşfetmeyi, yazmayı ve kendimi geliştirmeyi seviyorum.

ıletısım adreslerım