Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124

Bir sistemi hızlandırmak istediğinde akla gelen ilk tekniklerden biri caching olur. Bu yazıda cache’in ne işe yaradığını, neden her şeyi cache’e koyamadığımızı, cache hit oranının neden kritik olduğunu ve yanlış cache politikasının sistemi nasıl yavaşlatabileceğini öğreneceksin. Aslında bu çok basit: cache, tekrar tekrar yapılan işi azaltmak için sonucu bir yerde saklama fikridir. Ama işin zor kısmı “neyi saklayacağım, ne zaman güncelleyeceğim, dolunca neyi çıkaracağım?” sorularında başlar.
Bir sistemi hızlandırmak istediğinde akla gelen ilk tekniklerden biri caching olur. Bu yazıda cache’in ne işe yaradığını, neden her şeyi cache’e koyamadığımızı, cache hit oranının neden kritik olduğunu ve yanlış cache politikasının sistemi nasıl yavaşlatabileceğini öğreneceksin.
Aslında bu çok basit: cache, tekrar tekrar yapılan işi azaltmak için sonucu bir yerde saklama fikridir. Ama işin zor kısmı “neyi saklayacağım, ne zaman güncelleyeceğim, dolunca neyi çıkaracağım?” sorularında başlar.
Cache, sık kullanılan verinin daha hızlı erişilebilen bir yerde tutulmasıdır. Yani şöyle düşün: veritabanı ana kaynak olabilir ama her istekte aynı pahalı sorguyu çalıştırmak yerine, sık istenen sonucu bellekte tutarsın.
Basit bir sistemde istemci sunucuya istek gönderir, sunucu veritabanından veriyi alır ve sonucu geri döner. Cache yoksa her istek aynı yolu izler.
sequenceDiagram
participant Kullanıcı as Kullanıcı
participant Sunucu as Sunucu
participant Veritabanı as Veritabanı
Kullanıcı->>Sunucu: Veri isteği
Sunucu->>Veritabanı: Sorgu çalıştır
Veritabanı-->>Sunucu: Sonuç
Sunucu-->>Kullanıcı: YanıtBu diyagram cache olmayan klasik okuma akışını gösterir. Her istek veritabanına gider, bu da özellikle yoğun trafikte hem gecikmeyi hem de veritabanı yükünü artırır.
Cache eklendiğinde sunucu önce hızlı depoya bakar. Veri oradaysa veritabanına gitmeden yanıt dönebilir.
flowchart TD
A["Kullanıcı isteği"] --> B["Sunucu"]
B --> C{"Veri cache içinde mi?"}
C -->|"Evet"| D["Cache'ten oku"]
D --> E["Kullanıcıya yanıt dön"]
C -->|"Hayır"| F["Veritabanından oku"]
F --> G["Cache'i güncelle"]
G --> EBurada temel fark, veritabanının her istekte zorunlu durak olmaktan çıkmasıdır. Cache hit olduğunda, yani istenen veri cache’te bulunduğunda, sistem daha az iş yaparak daha hızlı cevap verir.
Cache genellikle uygulamaya veya kullanıcıya daha yakındır. Bunu şöyle hayal edebilirsin: markete gitmek yerine sık kullandığın şeyleri mutfak dolabında tutmak gibi. Her şeyi dolaba koyamazsın ama en sık kullandıkların oradaysa günlük işin hızlanır.
Cache’in sağladığı kazanç özellikle tekrar eden okumalarda ortaya çıkar. Bir ürün kataloğu, profil bilgisi, konfigürasyon, haber akışı, popüler içerik veya sık kullanılan hesaplama sonucu cache için iyi aday olabilir.
flowchart LR
subgraph "Yavaş Yol"
A["Sunucu"] --> B["Veritabanı sorgusu"]
B --> C["Yanıt"]
end
subgraph "Hızlı Yol"
D["Sunucu"] --> E["Bellek içi cache"]
E --> F["Yanıt"]
endDiyagram iki farklı okuma yolunu karşılaştırıyor. Veritabanı yolu daha güvenilir ana kaynak olsa da cache yolu daha kısa ve daha hızlıdır.
Küçük ve çoğunlukla değişmeyen veri kümelerinde bu mantıklı olabilir. Ama büyük sistemlerde veri terabaytlarca, hatta petabaytlarca olabilir. Bellek pahalıdır ve sınırlıdır.
Bu yüzden cache kullanırken asıl amaç her şeyi saklamak değil, en çok işe yarayan kısmı saklamaktır. Teknik terimle buna cache hit rate denir. Yani şöyle düşün: gelen isteklerin ne kadarı cache’ten cevaplanabiliyor?
flowchart TD
A["Tüm veri kümesi"] --> B["Sık erişilen küçük bölüm"]
A --> C["Nadiren erişilen büyük bölüm"]
B --> D["Cache'e alınır"]
C --> E["Veritabanında kalır"]
D --> F["Yüksek hit rate"]
E --> G["Gerektiğinde DB sorgusu"]Bu yapı cache’in seçici çalışması gerektiğini anlatır. İyi bir cache, sınırlı alanını en yüksek faydayı üretecek veriler için kullanır.
Cache’in kapasitesi dolduğunda yeni veri için yer açmak gerekir. İşte burada eviction policy devreye girer. Yani şöyle düşün: küçük bir rafın var ve yeni bir kitap koymak istiyorsun; hangi kitabı kaldıracağına karar vermen gerekir.
En bilinen politikalardan biri LRU’dur, yani Least Recently Used. En uzun süredir kullanılmayan veri çıkarılır. Bir diğeri LFU’dur, yani Least Frequently Used. En az kullanılan veri çıkarılır.
flowchart TD
A["Yeni veri cache'e eklenecek"] --> B{"Cache dolu mu?"}
B -->|"Hayır"| C["Veriyi cache'e koy"]
B -->|"Evet"| D{"Eviction politikası ne?"}
D -->|"LRU"| E["En uzun süredir kullanılmayan veriyi çıkar"]
D -->|"LFU"| F["En az kullanılan veriyi çıkar"]
E --> G["Yeni veriyi ekle"]
F --> GBu diyagram cache dolduğunda karar mekanizmasının nasıl çalıştığını gösterir. Politika yanlış seçilirse cache hız kazandırmak yerine sürekli veri çıkarıp ekleyen verimsiz bir katmana dönüşebilir.
Cache her zaman kazanç sağlamaz. Eğer istek deseni cache politikasına ters düşüyorsa sistem sürekli cache miss yaşar. Cache miss, istenen verinin cache’te bulunmaması demektir. Yani şöyle düşün: her seferinde çekmeceye bakıyorsun ama aradığın şey orada değil; sonra gidip asıl depodan alıyorsun.
Bu durumda cache kontrolü fazladan bir adım haline gelir. Daha kötüsü, cache sürekli dolup boşalırsa buna thrashing denir.
stateDiagram-v2
[*] --> CacheKontrolu
CacheKontrolu --> CacheHit: "Veri bulundu"
CacheKontrolu --> CacheMiss: "Veri yok"
CacheHit --> YanıtDon
CacheMiss --> VeritabanindanOku
VeritabanindanOku --> EvictionGerekliMi
EvictionGerekliMi --> VeriCikar: "Cache dolu"
EvictionGerekliMi --> CacheGuncelle: "Yer var"
VeriCikar --> CacheGuncelle
CacheGuncelle --> YanıtDon
YanıtDon --> [*]Bu durum diyagramı bir okuma isteğinin cache içinde nasıl ilerlediğini gösterir. Çok fazla cache miss ve eviction varsa cache katmanı fayda üretmek yerine sisteme ek maliyet bindirir.
Cache çoğu zaman veritabanının bir kopyasını tutar. Ama veri değiştiğinde kopyanın da değişmesi gerekir. Teknik terimle burada consistency problemi ortaya çıkar. Bunu şöyle hayal edebilirsin: aynı notun biri defterde, biri telefonda duruyor; birini değiştirip diğerini güncellemezsen iki farklı gerçek oluşur.
Bazı sistemlerde cache’in birkaç saniye veya dakika geriden gelmesi kabul edilebilir. Örneğin sayaçlar, izlenme değerleri veya öneri listeleri anlık birebir doğruluk istemeyebilir. Ama ödeme, bakiye veya sipariş durumu gibi alanlarda stale data, yani eski veri, ciddi hata doğurabilir.
flowchart TD
A["Veri güncelleme isteği"] --> B["Veritabanını güncelle"]
B --> C{"Cache ne zaman güncellenecek?"}
C -->|"Hemen"| D["Write-through yaklaşımı"]
C -->|"Sonra"| E["Eventual consistency"]
C -->|"Sil ve sonra yeniden doldur"| F["Cache invalidation"]
D --> G["Daha tutarlı ama daha yavaş yazma"]
E --> H["Daha hızlı ama geçici eski veri riski"]
F --> I["Sonraki okumada taze veri yüklenir"]Bu diyagram cache güncelleme stratejilerini basitleştirerek gösterir. Hangi yaklaşımın doğru olduğu sistemin doğruluk ihtiyacına, trafik yoğunluğuna ve kabul edilebilir gecikmeye bağlıdır.
Cache sadece sunucunun içinde olmak zorunda değildir. Uygulama belleğinde olabilir, veritabanının kendi içinde olabilir, istemci tarafında bulunabilir ya da bağımsız bir distributed cache sistemi olarak çalışabilir.
Distributed cache, yani dağıtık cache, ayrı bir servis gibi çalışır. Yani şöyle düşün: birçok uygulama sunucusunun ortak kullandığı hızlı bir veri katmanı vardır. Redis ve Memcached bu alanda sık kullanılan gerçek teknolojilerdir.
flowchart LR
A["Mobil / Web istemci"] --> B["Uygulama sunucusu"]
subgraph "Cache Katmanları"
C["Client cache"]
D["In-memory cache"]
E["Distributed cache"]
F["Database internal cache"]
end
B --> D
B --> E
B --> G["Veritabanı"]
G --> F
A --> CBu diyagram cache’in sistemin farklı noktalarına yerleşebileceğini gösterir. Büyük ölçekli sistemlerde çoğu zaman tek bir cache değil, birden fazla cache katmanı birlikte kullanılır.
Cache sadece “hızlı olsun” diye eklenen basit bir optimizasyon değildir. Doğru kullanıldığında veritabanı yükünü azaltır, kullanıcı deneyimini iyileştirir ve sistemin daha fazla trafiği kaldırmasını sağlar. Yanlış kullanıldığında ise eski veri, gereksiz bellek tüketimi, thrashing ve daha karmaşık hata senaryoları üretir.
Bu yüzden cache tasarlarken üç soruyu net sormak gerekir: hangi veri cache’e alınacak, veri değişince cache nasıl güncellenecek, alan dolunca hangi veri çıkarılacak? Bu üç karar, cache’in gerçekten işe yarayıp yaramayacağını belirler.
Caching, tekrar eden işi azaltmak için veriyi daha hızlı erişilen bir yerde saklama fikridir. Okuma yoğun sistemlerde gecikmeyi ciddi biçimde düşürebilir ve veritabanı üzerindeki baskıyı azaltabilir. Ama cache sınırsız değildir; doğru veri seçimi, iyi bir eviction politikası ve tutarlılık stratejisi gerekir. Kısacası cache sistemi hızlandırabilir, fakat ancak erişim desenlerini ve veri doğruluğu ihtiyacını doğru anlarsan gerçekten fayda sağlar.
Bu yazı Caching in distributed systems: A friendly introduction videosundan ilham alınarak yazılmıştır.