Yazılım dünyasına ilk adımımı attığım 2005 yılından bu yana, bilgisayar başında sabahladığım sayısız gecede öğrendiğim en net derslerden biri şudur: Kod çalışır, ama performans yaşatır. Özellikle veritabanı tarafında bu durum, projenin kaderini belirleyen ince bir çizgidir. Hepimizin başına gelmiştir; yerelde (localhost) harika çalışan, "fişek gibi" dediğimiz o proje, canlıya (production) alındığında ve içerisine gerçek veriler girmeye başladığında aniden kağnı arabasına döner. Müşteri arar, "Ekran gelmiyor, sistem dondu" der. Siz o sırada soğuk terler dökerek loglara bakarsınız ve acı gerçekle yüzleşirsiniz: Veritabanı cevap veremiyor.
Geçenlerde Pixel Lab® bünyesinde devraldığımız eski bir projede tam olarak bunu yaşadım. Milyonlarca satırlık bir "siparişler" tablosu vardı ve basit bir tarih aralığı sorgusu bile 12-13 saniye sürüyordu. Düşünebiliyor musunuz? Kullanıcı bir butona basıyor ve 13 saniye boyunca beyaz ekrana bakıyor. Bu, dijital dünyada bir ömür demek. Sorunu incelediğimde, tablonun tamamen çıplak olduğunu, yani üzerinde hiçbir stratejik indeksin bulunmadığını fark ettim. Doğru bir analiz ve birkaç stratejik CREATE INDEX komutuyla o 13 saniyelik sorguyu 40 milisaniyeye düşürdüğümde hissettiğim o rahatlama duygusunu tarif etmem zor. Tıpkı ormanda kaybolmuşken bir anda patikayı bulmak gibi.
Bugün, o "sihirli" dokunuşun arkasındaki mühendislikten, yani veritabanı indeksleme stratejilerinden ve büyük veriyi nasıl hızlı okuyabileceğimizden bahsetmek istiyorum. Çünkü bu iş, sadece "indeks ekle geç" diyebileceğimiz kadar basit değil.
Veritabanı İndeksleme Mantığı: Samanlıkta İğne Aramamak
İndekslemenin ne olduğunu en basit haliyle anlatmak için hep kütüphane veya kitap örneğini veririm. Elinizde 1000 sayfalık, teknik terimlerle dolu kalın bir kitap olduğunu düşünün. Ben size "Bana 'Polimorfizm' kelimesinin geçtiği sayfayı bul" desem ne yaparsınız? Eğer kitabın arkasında bir "İndeks" (Dizin) bölümü yoksa, 1. sayfadan başlayıp 1000. sayfaya kadar tek tek okumanız gerekir. Veritabanı dünyasında biz buna Full Table Scan (Tam Tablo Taraması) diyoruz ve bu, veritabanı sunucunuzun kabusudur.
Ancak kitabın arkasında bir indeks varsa, "P" harfine gidersiniz, "Polimorfizm"i bulursunuz ve yanındaki sayfa numarasını (örneğin Sayfa 452) alıp doğrudan o sayfayı açarsınız. İşte veritabanı indeksleri de tam olarak bu işi yapar. Verinin kendisini değil, veriye giden yol haritasını tutar.
B-Tree Yapısı: Ağacın Dallarında Gezinmek
Teknik tarafa biraz daha derinlemesine daldığımızda, modern ilişkisel veritabanlarının (MySQL, PostgreSQL, Oracle vb.) çoğunun varsayılan olarak kullandığı yapı B-Tree (Balanced Tree) yapısıdır. Bu yapıyı anlamak, neden bazı sorguların hızlı, bazılarının yavaş olduğunu kavramak için kritiktir.
B-Tree'yi ters duran bir ağaç gibi düşünebilirsiniz. Kök (Root) yukarıdadır ve dallar (Leaf nodes) aşağıya doğru yayılır. Veritabanı bir kaydı ararken, milyonlarca satırı tek tek gezmek yerine, ağacın tepesinden başlar. Aradığınız değer, mevcut düğümdeki değerden büyük mü küçük mü? Büyükse sağ dala, küçükse sol dala gider. Bu sayede her adımda veri setinin yarısını (veya ağacın yapısına göre büyük bir kısmını) eler.
Milyonlarca kayıt içinde aradığınız veriye ulaşmak için belki de sadece 3-4 kez zıplamanız (hop) yeterli olur. Bu, disk I/O (okuma/yazma) maliyetini inanılmaz düşürür. Benim doğa tutkumla bağdaştırırsam; ormanda rastgele yürümek yerine, elinizdeki pusula ve harita ile doğrudan hedefe yürümek gibidir.
Stratejik Hamleler: Neyi, Nasıl İndekslemeli?
Burada sıkça yapılan bir hatadan bahsetmek istiyorum. Genç geliştirici arkadaşlarım bazen indekslerin gücünü keşfedince, tablodaki her kolona indeks ekleme eğilimine giriyorlar. "Ne kadar çok indeks, o kadar hız" diye düşünüyorlar. Ama kazın ayağı öyle değil. İndeksleme bir strateji oyunudur.
Benim bir projeye başlarken veya optimize ederken dikkat ettiğim temel noktalar şunlardır:
- Kardinalite (Cardinality) Yüksek Kolonlar: İndeksleme, birbirinden farklı değerlerin çok olduğu kolonlarda en iyi sonucu verir. Örneğin
TC_Kimlik_No,EmailveyaSiparis_Kodugibi alanlar mükemmel adaylardır. AncakCinsiyet(Kadın/Erkek) veyaDurum(Aktif/Pasif) gibi sadece birkaç farklı değer içeren kolonları tek başına indekslemek genellikle veritabanı motoru (optimizer) tarafından göz ardı edilir. Çünkü verinin yarısını getirecekse, motor "İndeksle uğraşmam, hepsini okurum daha iyi" der. - WHERE, JOIN ve ORDER BY Kriterleri: Sadece
SELECTettiğiniz kolonları değil; filtreleme (WHERE), tablolari birleştirme (JOIN) ve sıralama (ORDER BY) yaptığınız kolonları indekslemelisiniz. Sorgu optimizasyonu aslında bu üçlünün dansıdır.
Bileşik İndeksler (Composite Index) ve Soldan Sağa Kuralı
Burası işin biraz daha "ustalık" gerektirdiği kısım. Çoğu zaman sorgularımız tek bir kritere dayanmaz. Örneğin:
SELECT * FROM siparisler WHERE musteri_id = 542 AND siparis_tarihi > '2023-01-01';
Bu sorgu için hem musteri_id hem de siparis_tarihi kolonuna ayrı ayrı indeks koymak her zaman en iyi sonucu vermez. Veritabanı motoru bu iki indeksten birini seçmek zorunda kalabilir veya ikisini birleştirirken (index merge) zaman kaybedebilir. Bunun yerine, bu iki kolonu kapsayan tek bir Bileşik İndeks (Composite Index) oluşturmak çok daha etkilidir.
Ancak burada dikkat etmeniz gereken "En Soldaki Önek" (Leftmost Prefix) kuralı vardır. Eğer indeksinizi (musteri_id, siparis_tarihi) şeklinde oluşturursanız:
- Sadece
musteri_idile yapılan sorgularda bu indeks çalışır. - Hem
musteri_idhemsiparis_tarihiile yapılan sorgularda çalışır. - Ancak: Sadece
siparis_tarihiile yapılan sorgularda bu indeks ÇALIŞMAZ.
Bu yüzden indeks sırasını belirlerken, sorgularınızda en sık ve en filtreleyici olan kolonu en başa koymalısınız. Ben genellikle projelerimde sorgu loglarını inceleyip, hangi kombinasyonların daha sık kullanıldığını analiz etmeden bileşik indeks oluşturmam.
Yanlış İndekslemenin Bedeli: Kaş Yaparken Göz Çıkarmak
Hadi dürüst olalım, her güzel şeyin bir bedeli vardır. İndekslerin bedeli de yazma performansı (Write Performance) ve disk alanıdır.
Siz bir tabloya her INSERT, UPDATE veya DELETE işlemi yaptığınızda, veritabanı sadece veriyi güncellemekle kalmaz; aynı zamanda o tabloya bağlı olan tüm indeks ağaçlarını da yeniden düzenler. Dallar değişir, yapraklar güncellenir. Eğer bir tabloda 10 tane indeksiniz varsa, basit bir kayıt ekleme işlemi arka planda 11 ayrı yazma işlemine dönüşür (1 veri + 10 indeks).
Geçmişte bir e-ticaret projesinde, log tablolarına gereksiz yere 6-7 tane indeks eklenmiş bir yapıyla karşılaşmıştım. Sistem o kadar yoğun log yazıyordu ki, indeksleri güncellemekten veritabanı kilitleniyordu (deadlock). İndeksleri temizleyip sadece hayati olanı bıraktığımızda, yazma hızı %300 arttı. Okuma hızı ise değişmedi çünkü o silinen indeksler zaten kullanılmıyordu.
Ayrıca disk alanı maliyeti de var. Büyük veride indekslerin boyutu, bazen verinin kendi boyutunu geçebilir. 100 GB'lık bir veriniz varken, 120 GB indeks dosyanız olabilir. SSD disklerin pahalı olduğu bulut sunucu ortamlarında bu, ay sonunda faturanıza yansıyan gereksiz bir maliyettir.
Sadelik Performansın Anahtarıdır
Yazılımda, tıpkı hayatta ve doğada olduğu gibi, sadelik en büyük güçtür. Karmaşık indeks yapıları, her kolona saldırırcasına indeks eklemek veya veritabanı motorunun işini yapmasına izin vermemek, uzun vadede başınızı ağrıtır. Benim Pixel Lab®'da benimsediğim felsefe şudur: Sadece ihtiyacın olanı kullan, fazlasını değil.
Veritabanı optimizasyonu, bir kez yapıp bitireceğiniz bir iş değildir. Proje yaşar, veri büyür, kullanıcı alışkanlıkları değişir ve sorgular evrilir. Bugün mükemmel çalışan bir indeks stratejisi, altı ay sonra veriniz iki katına çıktığında yetersiz kalabilir. Bu yüzden arada sırada o koda geri dönmek, EXPLAIN komutunu kullanarak sorguların ne yaptığını kontrol etmek ve veritabanınızın nabzını tutmak gerekir.
Eğer şu an projenizde yavaş çalışan bir sayfa, dönüp duran bir yükleme ikonu varsa, koda kızmadan önce veritabanınıza bir şans verin. Belki de tek ihtiyacı olan, doğru yere koyulmuş küçük bir yol haritasıdır.
Yorumlar
Henüz yorum yapılmamış. İlk yorumu siz yazın!
Yorum Yaz