SQL Hızlandırma
İpuçları
Bu konuda birçok şey yazılabilir ve hala eksik kalabilir
öncelikle onu belirtmek isterim.Ve DB’den DB’ye farkedebilir bazı şeyler ama
biz genel olarak karşılaştığımız sorunları ve çözümleri üzerinden ilerleyelim
umarım faydalı olur.
Bazı yöntemlerde sorgunuzu değiştirmeniz yeterli
olabiliecekken bazılarında sizin veya DBA’nizin tablolara müdahele etmesi
gerekebilir bunu unutmayın.
İpuçları yazmadan önce bahsetmek istediğim başka şeyler
var.Bir SQL sorgusu nasıl çalışır bunu anlatmak istiyorum.
Bir sorguyu çalıştırdığınızda önce hata var mı diye kontrol
edilir bu adımlar geçtikten sonra bu sorguyu çalıştıracak “motora”
gönderilir.Fakat burada şöyle bir sıkıntı var sizin gönderdiğiniz sorgu
mantıksal bir işlemdir bunun fiziksel olarak nasıl olacağına hangi yollar
izlenerek bu sorgunun çalıştırıacağını “motor” birçok olası plan içinden
seçer.Demekki biz bir sorguyu çalıştırdığımız anda bir çok plan oluşuyor en
uygun ise en az maliyetli olan oluyor.Bu maliyet verdiğiniz
hintlere,istatistiklere,indexlere bağlı olabiliyor.O yüzden her seferinde aynı
sorguyu aynı plan ile çalıştırmayabilir bunu unutmayın.Biz hızlandırma derken
aslında bu maliyeti düşürmeye çalışıyoruz 😊
·
Mümkün olduğunda ufak data setleri ile çalışın.
o
Bunu hemen biraz açmak istiyorum örneğin 50
sütunlu 100 milyon satırlı bir tablo ile 20 sütunlu 20 milyonluk iki tabloyu joinleyeceksiniz
ama ilk tablodan sadece 1m diğerinden sadece 500k’lık bir kitleyi
joinleyeceksek önce filtreleyip sonra joinlemek daha mantıklı olacaktır.
o
Bir diğer örnek ise örneğin bu iki tabloyu
joinlediniz bir subqueryde üstte başka bir tablo ile joinleyeceksiniz sadece
gerekli sütunları çağırın subquery’nin selectinde.Her sütun ek maliyet
demektir.
·
Bir data setini bir kere kullanmaya dikkat edin.
o
Buna da hemen bir örnek verelim.Örneğin son 30
gün son 15 gün ve son 7 günün satış rakamlarına ihtiyacınız var.Bu durumda bu
sorguyu 2 şekilde yazabilirsiniz.
Select
son30gun_satis,
son15gun_satis,
son7gun_satis
From
(
select
count(*) son30gun_satis from satis_tablosu where
satis_tarihi>=current_date-30
) son30gun
Left
join
(
select
count(*) son15gun_satis from satis_tablosu where
satis_tarihi>=current_date-15
) son15gun on 1=1
Left
join
(
select
count(*) son7gun_satis from satis_tablosu where
satis_tarihi>=current_date-15
) son7gun on 1=1
Dikkat
ederseniz satis_tablosunu 3 kere okudum bu x3 maliyet demektir.Peki ideal
yöntem nedir?
select
count(*)
son30gun_satis ,
count(case
when satis_tarihi>=current_date-15 then id else null end ) son15gun_satis ,
count(case
when satis_tarihi>=current_date-7 then id else null end ) son7gun_satis
from
satis_tablosu
where
satis_tarihi>=current_date-30
Gördüğünüz üzere tabloyu sadece 1 kere okudum.Unutmayın en çok maliyetlerden
biri okuma sırasında meydana gelir.
o
Bir diğer önemli tek seferde okuma ise
with’dir.Bir diğer ismi CTE(Common Table Expression).Eğer aynı subquery birden
çok yerde kullanılacaksa bu durumda WITH ile hafızaya alıp tek bir kere okumak
yine aynı okuma,join ve dönüşüm işlemlerini tekrar yapmakdan daha kısa
sürecektir.Hemen bir örnek kod verelim.
Select
*
From table
as aa
Left
join (bir çok join ve dönüşüm içeren aynı subquery) as bb on aa.id=bb.id
Left
join (bir çok join ve dönüşüm içeren aynı
subquery) as cc on aa.id=cc.id
Bunun yerine kod şöyle
yazılmalıdır.Böylelikle tek bir kez hesaplama yapılır.
With
subquery_hafizada as (bir çok join ve dönüşüm içeren aynı subquery)
Select
*
From table
as aa
Left
join subquery_hafizada as bb on aa.id=bb.id
Left
join subquery_hafizada as cc on aa.id=cc.id
·
Where veya Joinde kullandığınız sütunları
dönüşüm işlemlerine sokmayın.
Hemen
bir örnek verelim.Aşağıdaki örnekte sütun1 indexli dahi olsa indexi
kullanmayacaktır.Buda sorgunuzun performansını ciddi olarak azaltacaktır.Çünkü
full scan yapacaktır.
Select
*
From table
Where
CAST(sütun1 as DATE)=’2024-07-01’
Veya diğer bir örnek
Select
*
From table1
Left
join table2 on table1.Ad=table2.Ad+table2.Soyad
·
ORDER BY kullanmayın!
o
İnanılmaz performans öldürücü methodtur zorunlu
haller dışında kesinlikle kullanmayın yada çok ufak data setlerinde.
·
DISTINCT kullanmayın!
o
Çok ciddi performans düşürüdür kesinlikle
kullanmayın dupliceliği çözebiliyorsanız çözün.
·
UNION kullanmayın
o
DISTINCT yaptığından yine performansı çok ciddi
etkiler.
·
Indexli sütunlar kullanın.
o
Hem join hem wherede mümkün olduğunca sadece bu
sütunları kullanmaya çalışın.Eğer yoksa yaratın çekinmeyin.
o
Fakat şunu unutmayın çok fazla index ciddi yer
kaplar diskinizi doldurur herşeye index atılmamalıdır.
o
Eğer cadinatlity %30 üzerinde ise index mantıklı
olabilir.Yani sadece 1 ve 0 olan bir sütuna index atmayın en az %30’u farklı
değerlere sahip sütunlara index atın.
·
Partitionlı sütunlar kullanın.
o
Özellikle DWH yapılaırnda sıklıkla tercih
ettiğimiz partitionlı tablolar partition sütunu kullanılmazsa yanıt bile
vermeyecektir çoğu zaman muhakkak where ve joinlerinize ekleyin.
·
İstatistikleri güncellemeyi unutmayın.
o
İstatistikler en uygun execution planı seçmek
için istatistiklere başvurur ne kadar güncel olursa o kadar uygun planı seçer.
·
IN yerine EXISTS kullanın.
o
IN yaptığınızda bir değeri bulduğunda dahi diğer
satırları aramaya devam ederken EXISTS’de direk budum diyip sonraki değeri
aramaya başlar o yüzden EXISTS her zaman daha hızlıdır.Hemen örnek bir kod
yazalım.
Select
*
From
table1
Where
sutun1 IN (select sutun2 from table2)
Bunun
yerine şöyle yazmak daha iyi olacaktır.
Select
*
From
table1
Where
exists (select 1 from table2 where table2.sutun2=table1.sutun1)
·
* kullanmayın.
o
Mümkünse hiç bir zaman selectlerinizde *
kullanmayın bunun yerine sadece istediğiniz belirli sütunları isteyin.
·
Temp Tablolar kullanın
o
Temp tablolar veya geçici süreyle yarattığınız
klasik tablolar kullanmaktan çekinmeyin.Özellikle ETL süreçlerinde sıklıkla
kullandığımız bir methodtur inanılmaz performans arttırır işiniz bitince
silersiniz.
o
Temp tablo olarak yarattığınızda session
düştüğünde otomatik silinecektir ama uzun süren temp tablo yaratmalarında
tavsiye etmem klasik olarak yaratın olurda session düşerse bir daha hesaplamak
için dakikalar harcamazsınız kendiniz sonrasında silersiniz zaten.
·
DBLink Kullanmayın
o
Bazen ihtiyaçdan yapılabiliyor ama performans
anlamında inanılmaz kötü mümkünse tabloyuda aynı veritabanınına alın network
kaynaklı çok sorun yaşabilirsiniz.
·
View Kullanmayın
o
Viewler select veya joinlerde kullandığınızda
her çağrılmada tekrar tekrar hesaplanıp geldiğinden önce view hesaplanıp
sonrasında sizin query’iniz çalışır.Mümkünse tabloya basın viewlerinizi
sonrasında tablodan çağırın bunun için elbette ETL süreci gerekir bunu
yapamıyorsanız viewler üzerinde de index oluşturulabiliyor bu incelenebilir
veya view’in performansı arttırılabilir.
·
Hintleri dikkatli olmak kaydı ile kullanın
o
Hintler hızlandıracağı kadar yavaşlatabilirde o
yüzden çok dikkatli olun.Özellikle Oracleda PARALLEL,APPEND veya sql serverda
NOLOCK,TABLOCKX gibi hintler çok yardımcı olurlar ama yerine göre tehlikelidir.
·
Table Scan yapıp yapmadığınıza dikkat edin
o
Eğer sorgunuz index veya partition kullanmıyorsa
execution plana baktığınızda table scan görebilirsiniz bu ciddi bir
sorundur.Eğer indexli bir tabloda indexli sütun üzerinden where yapmanıza
rağmen table scan yapıyorsa bu durumda index rebuild edilebilir veya indexli
sütunda dönüşüm yapılıp yapılmadığına bakılabilir.
·
Nested Loop Join kullanılıyor ise düzenleyin.
o
Çoğu DB’de 3 tip fiziksel join vardır.Bunlar
İnner veya Left’den çok farklıdır.Inner veya Left mantıksaldır nasıl ilişkiye
gireceklerini söylerken fiziksel join bunların nasıl okunup nasıl
eşleştirileceğini gösterir.Nested loop iç içe iki for döngüsü gibi
düşünebilir.bir tablodaki her bir kaydı diğer tabloda aratır.Hak vereceğiniz
üzere bu 1 büyük 1 çok küçük tablo için mantıklıdır eğer sizin 2 büyük tablonuz
nested loop yapıyorsa ya hintlerle bunu değiştirmesini zorlayabilirsiniz ya da
index ve istatistisklerle başarabilirsiniz.Genelde DB’ler kolay kolay 2 büyük
tablo için bu seçmez.
·
DML(DELETE,INSERT,UPDATE) log seviyesini en aza
indirin.
o
Özellikle DWH’da log çok az önemlidir veri kaybı
bizde kolay kolay söz konusu değildir
zaten kaynada vardır log minimum tutulaiblir bunun içinde Oracle’da APPEND,SQL
Serverda TABLOCKX hintleri kullanılabilir tabi DB seviyesinde de minimum
seçeneği seçilebilir.
·
DELETE yerine TRUNCATE edin.
o
Full tabloyu boşaltacaksanız TRUNCATE seçin log
tutmaz.
·
Her zaman PL/SQL,T-SQL’lerinizde row by row
değil bulk olarak işlemlerinizi yapın.
o
Örneğin bir tablonuz var 1m satır ve siz bir
sütunu update edeceksiniz bu durumda 1m’lik bir loop’a sokup tek tek değil tek
bir UPDATE sorgusunda bu işi yapın.
·
Like kullanmayın
o
Mümkünse like kullanmayın çünkü aranan string
değerdeki tüm karakterileri incelediğinden çok yavaş olacaktır.Fakat içinde
aratmayan sadece ‘XXX%’ bunun gibi yani
XXX ile başlayanlar dediğimizde çok sorun yaşamayız çünkü tüm string aranmaz
sadece ilk 3’e bakılır.
·
OR kullanmayın
o
Eğer OR ile yazmanız gereken bir ifade var ise
bunu iki ayrı ifade ile yazıp UNION’lamak daha mantıklı olacaktır.Çünkü indexli
olsalar dahi OR kullanımında indexler kullanılmayacaktır.Örnek verelim
Select
*
From
table1 where sütun1=x or sütun2=y
Bunun
yerine aşağıdaki daha mantıklı olacaktır.
Select
*
From
table1 where sütun1=x
Union
Select
*
From
table1 where sütun2=y
Hiç yorum yok:
Yorum Gönder