# Java Tabanlı Veri Sistemlerinde Yüksek Başarım

> Java ve Spring tabanlı veri sistemlerinde gecikme, verim, JVM, JPA/Hibernate, JDBC, HikariCP, SQL, indeksleme, önbellek, Kafka, PostgreSQL, yük testi ve kapasite planlamasını ölçüm ve azaltma ilkesiyle ele alan kapsamlı teknik not.

- Author: Muhammet Ali Köker
- Language: tr
- Canonical: https://alikoker.com.tr/java-tabanli-veri-sistemlerinde-yuksek-basarim
- Translation: https://alikoker.com.tr/en/high-performance-java-data-systems
- Published: 2025-05-21T00:00:00+03:00
- Modified: 2026-08-25T17:56:00+03:00
- Verified: 2026-08-25T17:56:00+03:00
- Type: article

Java Tabanlı Veri Sistemlerinde Yüksek Başarım

Bu not, gerçek kurumsal uygulamalar ve projeler üzerinde oluşan veri tabanı erişim katmanı ve indeksleme çalışmalarını, Java Persistence arge notlarını ve veri yoğun sistem tasarımındaki güncel ilkeleri tek çerçevede toplar. Amaç ayar listesi vermek değil, yük arttığında hangi katmanın neden sınır olduğunu ve değişikliğin bedelini gösterebilmektir.

Temel yöntem değişmez:

```text
ölç
darboğazı bul
tek değişken değiştir
aynı yükle yeniden ölç
```

Bir optimizasyon yalnız "daha hızlı" diye tanımlanamaz. İndeks okumayı hızlandırırken yazmayı pahalılaştırır, batch verimi artırırken tek kaydın bekleme süresini büyütebilir, önbellek gecikmeyi düşürürken tutarlılık yükü ekler, çoğaltma erişilebilirliği artırırken eski veri okuma riskini doğurur.


Bu sürümde notun ilk çerçevesi korunmuş, eksik kalan Spring Boot/JVM gözlemleme, yük testi, thread havuzları, HikariCP, reaktif programlama, Caffeine ve Redis, HTTP/2 ve HTTP/3, serileştirme, PostgreSQL işletimi, Kafka üretici/tüketici davranışı, kapasite planlama ve GraalVM başlıkları tamamlanmıştır. Sürüme bağlı ayrıntılar Ağustos 2026 itibarıyla birincil ürün belgeleriyle yeniden doğrulanmıştır.

Zen yaklaşımı burada bir üslup tercihi değil mühendislik kuralıdır:

```text
daha çok ayar değil
daha az iş

daha çok eşzamanlılık değil
ölçülmüş sınır

daha çok soyutlama değil
görünür maliyet
```

Bir değişiklik sistemin yaptığı işi azaltmıyor, kuyruğu görünür kılmıyor veya bir sınırı daha deterministik hale getirmiyorsa yalnız karmaşıklık ekliyor olabilir.

## 1. Başarım modeli

### Yanıt süresi ve verim

Yanıt süresi tek isteğin tamamlanma süresi, verim birim zamanda tamamlanan iş miktarıdır. Aynı sistem daha yüksek verime ulaşıp tek istek için daha yavaş çalışabilir.

Little yasası kararlı sistemde eşzamanlılığı verir:

```text
L = λ · W

L : sistemde aynı anda bulunan iş
λ : varış hızı
W : sistemde geçirilen ortalama süre
```

Bu bağıntı yalnız web isteği için değil bağlantı havuzu, kuyruk, işleyici ve veritabanı oturumu için de kullanılabilir.

Örnek:

```text
1000 istek/s
istek başına DB bağlantısı tutma süresi = 5 ms

L = 1000 × 0.005 = 5
```

Kuramsal ortalama beş eşzamanlı bağlantıdır; dalgalanma payı ayrıca eklenir. Bu sonuç, bin eşzamanlı HTTP isteği var diye bin bağlantı gerekmediğini gösterir.

### Doygunluk ve kuyruk

Basit bir tek-kuyruk modelinde:

```text
R = S / (1 - ρ)

R : bekleme dahil süre
S : hizmet süresi
ρ : kullanım oranı
```

Kaynak doygunluğa yaklaştıkça gecikme doğrusal artmaz. Yüzde 95 kullanım, yüzde 50 kullanıma göre küçük bir artış değil, kuyruk davranışının değişmesidir.

Üretim kapasitesi tepe noktasına göre değil, hata payı bırakılmış güvenli çalışma bölgesine göre seçilir. Doyma noktasında çalışan sistemin yedek kapasitesi yoktur; küçük trafik sıçraması, GC, yavaş sorgu veya ağ gecikmesi kuyruğu büyütür.

### Amdahl ve koordinasyon maliyeti

Amdahl yasası seri kalan bölümün toplam hızlanmayı sınırladığını söyler:

```text
S(N) = 1 / ((1-p) + p/N)
```

Dağıtık sistemlerde yalnız seri bölüm yoktur; düğümler birbirleriyle de koordinasyon kurar. Evrensel ölçeklenebilirlik yasası bu maliyeti ekler:

```text
C(N) = N / (1 + α(N-1) + βN(N-1))

α : serileşme
β : koordinasyon / tutarlılık
```

`β` sıfır değilse bir noktadan sonra düğüm eklemek toplam verimi düşürebilir. Aynı veritabanına yazan uygulama örneklerini artırmak, ortak kilit ve veri sayfaları değişmiyorsa yalnız yarışan istemci sayısını artırır.

### Uç gecikme

Ortalama yanıt süresi kuyruktaki kötü davranışı saklar. P95, P99 ve P99.9 üretim davranışını daha iyi gösterir.

Bir istek birden çok arka uç çağrısına bağlıysa tek bir yavaş çağrı bütün isteği yavaşlatır. Fan-out arttıkça uç gecikme kullanıcıya daha sık yansır.

Yüzdelikler ortalanmaz. Birden çok sunucunun P99 değerlerini toplamak veya ortalamak yerine histogramlar birleştirilir ve yüzdelik birleşik dağılımdan hesaplanır.

### Eşgüdümlü atlama

Yük üreteci bir isteğin bitmesini bekleyip sonra yenisini gönderiyorsa sistem yavaşladığında test de yavaşlar; gerçek hayatta kuyruğa girecek istekler hiç gönderilmez. Eşgüdümlü atlama (coordinated omission) P99'u olduğundan iyi gösterir.

Yük testi mümkün olduğunda açık döngülü varış hızını korumalı ve gecikmeyi planlanan gönderim anından ölçmelidir.

## 2. Güvenilirlik, ölçeklenebilirlik ve bakım kolaylığı

Başarım tek başına sistem kalitesi değildir. Veri yoğun uygulamada üç soru birlikte cevaplanır:

- Arıza olduğunda hizmet devam ediyor mu?
- Veri ve trafik büyüdüğünde davranış öngörülebilir mi?
- Sistem değiştirilebilir ve anlaşılabilir mi?

Bir bileşenin bozulması arızadır; kullanıcının beklediği hizmetin SLO dışına çıkması sistem başarısızlığıdır. Disk arızası çoğaltma ile maskelenebiliyorsa bileşen arızalanmış, hizmet başarısız olmamıştır.

Ölçeklenebilirlik "daha çok makine eklenebilir" demek değildir. Yük artışı tanımlanmalı, bu artışın hangi kaynak üzerindeki maliyeti büyüttüğü ölçülmeli ve mimarinin o maliyeti nasıl dağıttığı gösterilmelidir.

Bakım kolaylığı performansın karşıtı değildir. Anlaşılmaz bir hız hilesi, birkaç ay sonra yanlış değişiklikle kaybediliyorsa sürdürülebilir optimizasyon değildir.

## 3. Veri erişim yolunu bütün olarak görmek

Bir istek veritabanına tek adımda gitmez:

```text
HTTP isteği
-> uygulama thread'i
-> işlem sınırı
-> bağlantı havuzu
-> JDBC sürücüsü
-> ağ
-> SQL parse / plan
-> kilit ve MVCC
-> tampon önbelleği
-> indeks / tablo / depolama
-> sonuç kümesi
-> ORM eşleme
-> serileştirme
-> ağ yanıtı
```

Her ok ayrı bir maliyet sınırıdır. ORM'de görünen "repository çağrısı" altta onlarca SQL, binlerce satır, çok sayıda ağ gidiş-dönüşü veya uzun kilit beklemesi oluşturabilir.

Optimizasyon katman seçimiyle başlar. SQL 2 ms, bağlantı bekleme 150 ms ise sorgu optimizasyonu yanlış yerdedir. Sorgu 400 ms sürüyorsa thread sayısını artırmak aynı pahalı sorguyu daha çok eşzamanlı çalıştırır.

## 4. Ölçüm disiplini

### Dört gözlem katmanı

Aynı olayı dört katmanda görmek gerekir:

```text
uygulama      istek, transaction, sorgu sayısı
JVM           CPU, allocation, GC, thread bekleme
veritabanı    plan, satır, blok, kilit, redo/WAL
işletim sistemi CPU, bellek, disk, ağ
```

Tek katman, neden-sonuç bağını kurmaya yetmez.

### Uygulama metrikleri

İstek hızı, hata oranı ve gecikme dağılımı servis sağlığını; kaynak kullanımı, doygunluk ve hata sayısı altyapı sağlığını gösterir.

Yüksek kardinaliteli etiketler metrik sistemini bozar. Kullanıcı kimliği, ham URL, sorgu metni, istek kimliği ve serbest metin etiket yapılmaz.

Bağlantı havuzu için en az:

```text
aktif bağlantı
boş bağlantı
bekleyen istek
bağlantı alma süresi
zaman aşımı
```

izlenir.

### JFR ve profil

Java Flight Recorder düşük ek yükle CPU, allocation, GC, kilit, park, ağ ve sanal thread olaylarını aynı zaman çizelgesinde gösterir. Sorun oluşmadan önce başlayan dönen kayıt, olay sonradan incelenecekse talep üzerine profilden daha değerlidir.

CPU profili yalnız çalışan kodu gösterir. Servis yavaş ama CPU düşükse duvar saati profiline bakılır; bekleme, kilit, ağ ve havuz gecikmesi burada görünür.

Allocation profili, yığın büyütmeden önce bakılacak yerdir. Gereksiz nesne üretimi düzeltilmeden GC ayarı yapmak maliyeti gizler.

### SQL gözlemi

Üç ayrı veri gerekir:

- uygulamanın gerçekten gönderdiği SQL ve parametreler,
- ORM'in sorgu/entity/flush sayıları,
- veritabanının gerçek yürütme planı ve gerçek satır sayıları.

`show_sql` geliştirmede yardımcı olabilir fakat süre, batch, bind ve toplam çağrı davranışını tek başına açıklamaz.

İstek başına sorgu sayısı testte sınırlandırılabilir. N+1 problemi üretimde değil testte kırılmalıdır.

### İş miktarı ile süreyi ayırmak

Süre önbellek sıcaklığı, eşzamanlı yük ve depolama durumuna göre değişir. Okunan blok sayısı, döndürülen satır, ağda taşınan bayt ve gidiş-dönüş sayısı iş miktarını daha kararlı gösterir.

İyileştirme şu soruya cevap vermelidir:

```text
daha az ne yaptık?
```

Cevap yoksa kazanç çoğu zaman geçicidir.

### Micrometer, histogram ve kardinalite

Spring Boot tarafında ölçüm yalnız bir `Timer` eklemek değildir. Micrometer sayaç, gauge, timer ve dağılım özeti gibi ölçüleri ortak bir modelde taşır; Prometheus gibi sistemlerde gecikmenin birleştirilebilir biçimi histogramdır.

İstemci tarafında önceden hesaplanan P95/P99 değerleri farklı instance veya etiket kümeleri arasında matematiksel olarak toplanamaz. Histogram bucket'ları ise uygun boyutlar üzerinden toplanıp yüzdelik sonradan hesaplanabilir. Bu nedenle çok örnekli sistemde amaç "her instance P99 yazsın" değil, aynı SLO sınırlarını taşıyan bir dağılım üretmektir.

Histogram da bedelsiz değildir. Her bucket ve her tag birleşimi yeni zaman serisi üretir. Özellikle:

```text
userId
requestId
ham URI
SQL metni
serbest hata mesajı
```

etiket yapılırsa gözlem sistemi uygulamanın kendisinden önce doygunluğa girebilir. SLO bucket'ları ve beklenen minimum/maksimum değerler gerçek iş yüküne göre sınırlandırılır.

RED servis yolunu, USE kaynak yolunu tamamlar:

```text
RED: rate, errors, duration
USE: utilization, saturation, errors
```

Bir API'nin P99'u yükselirken CPU düşükse USE tarafında bağlantı havuzu, disk, ağ veya lock doygunluğu aranır. CPU yüksek ama istek hızı sabitse allocation, serileştirme, plan değişimi veya sıcak kod yolu incelenir.

### async-profiler ve flame graph

JFR olay zaman çizelgesini ve JVM bağlamını güçlü biçimde verir; async-profiler ise CPU, allocation, wall-clock ve lock örneklemesiyle sıcak stack'leri düşük ek yükle görünür kılar. İki araç rakip değildir.

CPU flame graph çalıştırılan kodu gösterir. Wall-clock profilinde ağ, dosya, lock, park, bağlantı bekleme ve diğer bekleme süreleri görünür. Allocation flame graph ise GC semptomundan önce hangi çağrı yolunun nesne ürettiğini gösterir.

Flame graph genişliği süre veya örnek payıdır; "üstte görünen metod kötüdür" diye okunmaz. Önce geniş taban bulunur, stack yukarı izlenir, değişiklikten sonra aynı yükle ikinci profil alınır. Diferansiyel profil, değişikliğin gerçekten hangi maliyeti azalttığını gösterir.

### Sürekli kayıt ve olay anı

Üretimde yalnız sorun çıktıktan sonra profiler açmak, kazadan sonra kamera takmaya benzer. JFR'nin dönen kaydı düşük ek yükle sürekli tutulabilir; olay olduğunda son dakikalar dump edilir. Ağustos 2026 itibarıyla Oracle'ın JDK 25 belgeleri JFR'yi üretimde sürekli açık tutulabilecek kadar düşük ek yüklü bir araç olarak tanımlar.

Buna karşılık bütün araçlar sürekli açık bırakılmaz. Native Memory Tracking JVM içi native tahsisleri ayrıntılı gösterir fakat kapalı varsayılandır ve ölçülebilir ek yük getirir. NMT üçüncü taraf native kodu veya tüm JDK kütüphane tahsislerini eksiksiz izlemez. RSS büyümesi görüldüğünde:

```text
heap mi?
metaspace mi?
thread stack mi?
direct buffer mı?
JVM native mi?
JNI / başka native kütüphane mi?
```

soruları ayrı ayrı cevaplanır.


## 5. Bağlantı havuzu

### Bağlantı pahalıdır

Yeni bağlantı TCP oturumu, kimlik doğrulama, veritabanı oturumu ve bellek ayırma maliyeti taşır. Havuz bu maliyeti uygulama ömrüne yayar.

Havuz bağlantıyı hızlandırmaz; pahalı bağlantı kurulumunu yeniden kullanır ve veritabanına girebilecek eşzamanlılığı sınırlar.

### Havuz büyük olmak zorunda değildir

Veritabanının gerçek paralelliği CPU, depolama, kilitler ve ortak veri yapılarıyla sınırlıdır. Havuz bu sınırın çok üzerine çıkarsa kuyruk uygulamadan veritabanının içine taşınır.

Uygulama kuyruğu daha görünürdür:

```text
küçük havuz
-> ölçülebilir bekleme
-> kontrollü zaman aşımı
```

Aşırı büyük havuz:

```text
çok bağlantı
-> daha fazla eşzamanlı SQL
-> kilit / CPU / I/O çekişmesi
-> uzun kuyruk
-> uç gecikme
```

üretebilir.

### Boyutlandırma

Yaygın çekirdek tabanlı formüller yalnız başlangıç noktasıdır. Daha doğru başlangıç, ölçülen bağlantı tutma süresi ile Little yasasıdır.

Havuz birkaç farklı boyutta yük testine sokulur; en yüksek verimi en düşük P99 ile sağlayan bölge aranır. Optimum tek sayı değil, güvenli bir aralıktır.

### Zaman aşımı ve yaşam süresi

Bağlantı bekleme süresi sınırlı olmalıdır. Sonsuz bekleme, aşırı yükü istek kuyruğuna saklar.

Maksimum bağlantı ömrü, veritabanı veya ara ağ cihazlarının oturum ömründen kısa seçilebilir; bağlantıların aynı anda ölmemesi için yaşam sürelerine küçük rastgele sapma eklemek yeniden bağlanma fırtınasını azaltır.

Canlılık kontrolünde ağır `SELECT` yerine sürücünün desteklediği hafif doğrulama kullanılır.

Sızıntı algılama sürekli performans aracı değil, bağlantıyı gereğinden uzun tutan çağrı yollarını bulma aracıdır.

### Bağlantıyı geç al, erken bırak

İşlem içinde dosya okuma, uzak HTTP çağrısı veya uzun CPU hesabı varsa bağlantı boş yere tutulur.

```text
yanlış:
transaction başla
DB oku
uzak servis çağır
hesapla
DB yaz
commit

tercih:
gerekli veriyi kısa işlemde oku
işlem dışı hesapla / çağır
kısa işlemde yaz
```

Tutarlılık gereği bütün adımlar tek atomik işlem olamıyorsa çözüm bağlantıyı daha uzun tutmak değil, süreç tasarımını değiştirmektir.

### Çok uygulama örneği

Toplam bağlantı:

```text
uygulama örneği × örnek başına havuz
```

ile büyür. On uygulama örneğine ayrı ayrı yirmi bağlantı vermek veritabanında iki yüz oturum demektir.

Merkezi işlem havuzlayıcı bu sayıyı azaltabilir; bedeli oturum durumunun kaybolmasıdır. Geçici tablo, oturum değişkeni, oturuma bağlı hazırlanmış ifade veya oturum kilidi kullanan uygulamalarda işlem düzeyi havuzlama dikkat ister.


### HikariCP ayarlarını amaçlarıyla okumak

HikariCP'de önemli ayarlar birbirinin yerine geçmez:

```text
maximumPoolSize   DB'ye girebilecek üst eşzamanlılık
connectionTimeout havuz doluyken ne kadar bekleneceği
maxLifetime        bağlantının en uzun yaşamı
keepaliveTime      boş bağlantının canlı tutulma aralığı
validationTimeout  canlılık kontrolü bütçesi
```

`maximumPoolSize` uygulama thread sayısından türetilmez. HikariCP'nin kendi kılavuzu da havuz boyutunda "daha az çoğu zaman daha fazladır" ilkesini vurgular. Çekirdek tabanlı formüller ancak ilk deney noktasıdır; I/O, lock, SQL süresi, uygulama örnek sayısı ve veritabanı oturum maliyeti değiştikçe optimum bölge de değişir.

Havuz dolu olduğunda `getConnection()` sonsuza kadar beklememelidir. Bekleme bütçesi kullanıcı isteğinin toplam timeout bütçesinden küçük olmalıdır. Bağlantı alınamıyorsa bunu yeni thread veya yeni retry ile gizlemek yerine doygunluk olarak kabul etmek gerekir.

`maxLifetime`, veritabanı, firewall, load balancer veya ağ altyapısının bağlantı ömründen daha kısa seçilebilir. HikariCP bağlantıların aynı anda emekli olmaması için küçük bir sapma uygular. Keepalive yalnız boş bağlantılar üzerinde düşünülmeli ve `maxLifetime`'dan kısa olmalıdır. Sürücü JDBC4 `Connection.isValid()` desteği veriyorsa ağır bir test sorgusu yazmak yerine bu yol tercih edilir.

Havuz metriğinin en yararlı oranı yalnız `active/maximum` değildir. Aşağıdaki üç olay birlikte okunur:

```text
pending > 0
acquisition P99 yükseliyor
DB hizmet süresi yükseliyor mu, sabit mi?
```

DB hizmet süresi sabitken acquisition büyüyorsa havuz/uygulama kuyruğu sınırdır. DB hizmet süresi de büyüyorsa havuzu genişletmek çoğu zaman veritabanındaki çekişmeyi artırır.

## 6. Sanal thread ve gerçek eşzamanlılık sınırı

Sanal thread, bloke edici G/Ç sırasında taşıyıcı thread'i serbest bırakarak thread başına istek modelini yüksek eşzamanlılıkta ucuzlatır. CPU işini hızlandırmaz.

Bu nedenle:

```text
10 000 sanal thread
10 DB bağlantısı
```

olan sistemde gerçek veritabanı eşzamanlılığı yine yaklaşık ondur. Thread sınırı kalkınca bağlantı havuzu görünür hale gelir.

Çözüm havuzu otomatik büyütmek değildir. Giriş hızı sınırlandırılır, işlem süresi kısaltılır, havuz beklemesi ölçülür ve veritabanının güvenli eşzamanlılığı korunur.

Yeni JDK sürümlerinde monitör tabanlı sanal thread sabitlenmesinin önemli bölümü azaltılmış olsa da yerel çağrılar ve sürüme özgü davranışlar JFR olaylarıyla doğrulanmalıdır.

Reaktif programlama akış, yoğun fan-out ve uçtan uca geri basınç gereken yerde anlamlıdır. Bloke edici JDBC çağrısını reaktif zincire sarmak veritabanını asenkron yapmaz.


### Spring Boot 4 ve sürüm sınırı

Spring Boot 4'te sanal thread kullanımı Ağustos 2026 itibarıyla varsayılan değildir; `spring.threads.virtual.enabled=true` ile açılır. Açıldığında klasik task pool boyut ayarlarının önemli bölümü artık aynı anlamı taşımaz; sanal thread'ler JVM'nin ortak taşıyıcı havuzu üzerinde zamanlanır. Bu nedenle eski `core-size`, `max-size` veya Tomcat worker sayısını aynen taşıyıp sonucu sanal thread davranışı sanmak yanlıştır.

Sanal thread'ler daemon niteliğindedir. Uygulamanın yaşamını yalnız scheduled işlerin thread'lerine bağlayan süreçlerde Spring Boot'un `spring.main.keep-alive` davranışı ayrıca değerlendirilmelidir.

JDK 21 sanal thread'i standartlaştırdı. JDK 24'te JEP 491, `synchronized` içinde bloke olan sanal thread'lerin taşıyıcı thread'i bırakabilmesini sağlayarak monitör kaynaklı pinning'in önemli bölümünü ortadan kaldırdı. Bu, "pinning artık yok" demek değildir; native çağrılar, sürüme özgü kütüphane davranışı ve başka blokaj biçimleri JFR ile doğrulanır.

### @Async, scheduler ve kontrolsüz fan-out

`@Async` bir performans özelliği değil başka bir yürütme kuyruğudur. Scheduler da aynı şekilde kapasite üretmez. Bir scheduled iş tek thread üzerinde uzun sürüyorsa sonraki çalışmaları geciktirebilir; sanal thread açıkken ise thread sayısı ucuzladığı için aşağı akış kaynağını daha kolay taşırabilir.

Bu yüzden fan-out sınırı uygulama thread'iyle değil hedef kaynağın kapasitesiyle konur:

```text
10 000 ucuz sanal thread
-> 10 DB bağlantısı
-> 4 uzak servis kotası
-> 1 sıcak kilit
```

zincirinde en küçük güvenli sınır sistemi belirler.

### Structured Concurrency

Structured Concurrency, birbirine bağlı alt görevlerin yaşam döngüsünü tek iş birimi gibi yönetmeyi; iptal, hata yayılımı ve gözlemlenebilirliği sadeleştirmeyi amaçlar. Ancak JDK 26'da dahi preview API durumundadır. Üretim API sözleşmesi olarak sürümler arası kararlı kabul edilmemeli, kullanılıyorsa preview bağımlılığı bilinçli yönetilmelidir.

### WebFlux ile sanal thread aynı problem değildir

WebFlux non-blocking event loop ve Reactive Streams geri basıncıyla özellikle streaming, çok yüksek fan-out ve uçtan uca reaktif sürücü zincirinde anlamlıdır. Spring MVC + sanal thread ise bloke edici API'lerle thread-per-request programlama modelini daha ucuz hale getirir.

JDBC/JPA kullanan, sınırlı DB pool'a sahip tipik OLTP serviste WebFlux'a geçmek veritabanı çağrısını non-blocking yapmaz. R2DBC farklı sürücü ve transaction modelidir. Mimari seçim framework modasına göre değil bütün çağrı zincirinin bloklama modeline göre yapılır.

## 7. İşlem sınırları

### ACID'in fiziksel karşılığı

Atomiklik undo/geri alma bilgisiyle, dayanıklılık redo veya WAL ile, yalıtım kilit veya MVCC ile uygulanır. Tutarlılık ise tanımlanan kısıtların ve doğru uygulama mantığının sonucudur.

Commit, yalnız Java metodunun bitmesi değildir; dayanıklılık garantisi isteniyorsa günlük kaydının kalıcı ortama ulaşması gerekir. Grup commit birden çok işlemin fsync maliyetini paylaşarak verimi artırır.

### MVCC

MVCC okuyucuya tutarlı bir eski sürüm göstererek okuma ve yazmanın birbirini daha az engellemesini sağlar. Bedeli eski sürüm tutma ve temizleme maliyetidir.

Uzun transaction, yalnız kilit süresini değil eski sürümlerin ne kadar süre tutulacağını da artırabilir. "Salt okunur, zarar vermez" genellemesi bu nedenle doğru değildir.

### Yalıtım anomalileri

Temel anomaliler:

- kirli okuma,
- kirli yazma,
- tekrarlanamayan okuma,
- hayalet okuma,
- okuma çarpıklığı,
- kayıp güncelleme,
- yazma çarpıklığı.

Anlık görüntü yalıtımı birçok okuma anomalisini çözer fakat yazma çarpıklığını garanti olarak çözmez. Aynı iş kuralına dayanıp farklı satırları güncelleyen iki işlem, tek tek doğru görünürken birlikte kısıtı bozabilir.

Gerçek serileştirilebilirlik, eşzamanlı yürütmenin sonucu seri bir sırayla aynı olacak garantisini verir. Bunun maliyeti uygulamaya göre kilit bekleme veya çakışan işlemlerin iptal edilip yeniden denenmesidir.

### Yalıtım adı garanti değildir

`READ COMMITTED`, `REPEATABLE READ` veya `SERIALIZABLE` etiketlerinin ayrıntılı davranışı veritabanına göre değişebilir. Taşınabilir kod yalnız anotasyon adına güvenmez; gerekli iş kuralını hedef veritabanında test eder.

### İşlem kısa olmalıdır

Harici servis, kullanıcı etkileşimi, dosya aktarımı ve uzun hesap işlem içinde tutulmaz.

Kısa işlem:

- bağlantıyı erken bırakır,
- kilit süresini azaltır,
- MVCC eski sürüm baskısını düşürür,
- iyimser çakışma penceresini küçültür,
- hata sonrası yeniden denenecek işi azaltır.

## 8. Spring işlem yönetimi

`@Transactional` bir sözdizimi değil proxy davranışıdır. Aynı sınıf içindeki doğrudan çağrı proxy'den geçmiyorsa beklenen işlem sınırı oluşmayabilir.

Varsayılan geri alma kuralları istisna türüne bağlıdır; iş kuralı "bu hata atomikliği bozmalı" diyorsa `rollbackFor` gibi politika açık yazılır.

`REQUIRES_NEW` yeni mantıksal işlemden fazlasını yapabilir: ikinci fiziksel bağlantı gerektirir. Dış işlemler havuzdaki tüm bağlantıları tutarken iç işlemler yeni bağlantı beklerse uygulama kendi kendini kilitleyebilir.

`readOnly=true` yazmayı kriptografik olarak yasaklayan bir güvenlik mekanizması değildir. Sağlayıcıya flush ve izleme davranışı için ipucu verir; hedef sürümdeki gerçek etkisi ölçülmelidir.

Salt okunur işlemler, okuma kopyasına yönlendirme için de doğal işarettir; ancak kopya gecikmesi "kendi yazdığını oku" beklentisi olan akışlarda ayrıca ele alınır.

## 9. Eşzamanlılık denetimi

### İyimser kilitleme

Sürüm sütunu güncellemeye eklenir:

```sql
UPDATE account
SET balance = ?, version = 4
WHERE id = ? AND version = 3;
```

Etkilenen satır sıfırsa veri değişmiştir. Kilit beklemesi yoktur; çakışma düşükse en ucuz yöntemlerden biridir.

İyimser kilitleme yalnız tek satırın sürümünü korur. İş kuralı birden çok satır veya koleksiyon üzerinden kuruluyorsa kök sürümünün artırılması, benzersiz kısıt, serileştirilebilir işlem veya açık kilit gerekebilir.

### Kötümser kilitleme

Kötümser kilit kaynak üzerinde sıra oluşturur. Çakışma çok yüksek ve iş kısa ise yeniden deneme fırtınasından daha ucuz olabilir.

Kurallar:

```text
kilidi geç al
işi kısa tut
her yerde aynı sırayla kilitle
bekleme süresini sınırla
kilit altında uzak çağrı yapma
```

### Kilitlenme

Kilitlenme hata değil, eşzamanlı kilitli sistemin beklenen olayıdır. Veritabanı çevrimi bulur ve bir işlemi kurban seçer.

Uygulama:

- hatayı sınıflandırır,
- yalnız geçici hatayı yeniden dener,
- işlemi baştan çalıştırır,
- deneme sayısını sınırlar,
- üstel bekleme ve rastgele sapma kullanır.

### İdempotent yeniden deneme

Bir isteği iki kez çalıştırmak dış dünyada iki etki üretmemelidir. Ödeme, mesaj, dosya veya uzak servis çağrısı gibi yan etkiler işlem yeniden denemesinden ayrılır.

İdempotency anahtarı:

```text
aynı iş kimliği
-> ilk sonuç saklanır
-> tekrar çağrı aynı sonucu görür
```

modeliyle tekrar etkisini sınırlar.

## 10. Giden mesaj kutusu ve veritabanı dışı yan etkiler

Veritabanı işlemi ile mesaj kuyruğu veya HTTP çağrısını tek yerel transaction altında atomik yapmak genellikle mümkün değildir.

Giden mesaj kutusu (transactional outbox) yaklaşımı:

```text
aynı DB transaction:
  iş verisini yaz
  outbox kaydını yaz

ayrı süreç:
  outbox oku
  mesajı gönder
  gönderildi olarak işaretle
```

şeklindedir.

Mesaj en az bir kez ulaşabilir; tüketici idempotent olmalıdır. "Tam olarak bir kez" iddiası dış yan etkiler de dahil edildiğinde çoğu zaman protokol ve saklanan kimlikler üzerinden kurulmuş bir yanılsamadır.

## 11. Persistence context

Persistence context aynı kimliğe sahip varlığı işlem içinde tek Java nesnesiyle temsil eder, değişiklikleri toplar ve kirlilik denetimi yapar.

Bu kolaylık bedelsiz değildir:

```text
yönetilen varlık sayısı ↑
snapshot belleği ↑
flush taraması ↑
GC baskısı ↑
```

Uzun toplu işlerde context sınırsız büyütülmez.

### Flush ve clear

```java
for (int i = 0; i < items.size(); i++) {
    entityManager.persist(items.get(i));

    if ((i + 1) % batchSize == 0) {
        entityManager.flush();
        entityManager.clear();
    }
}
```

`flush()` SQL'i veritabanına gönderir, `clear()` yönetilen varlıkları ayırır, `commit` işlemi kalıcılaştırır. Üçü aynı kavram değildir.

### Salt okunur yol

Salt okunur işte varlık değişikliği izlenmeyecekse DTO veya scalar sonuç çoğu zaman entity yüklemekten daha ucuzdur. Entity gerekiyorsa read-only işlem ve sağlayıcı ipuçları gereksiz snapshot/flush maliyetini azaltabilir.

### persist ve merge

Yeni varlık için `persist`, ayrık nesnenin durumunu kopyalamak için `merge` kullanılır. `merge` gerektiğinde mevcut satırı yüklediği için toplu yazmada gizli `SELECT` maliyeti oluşturabilir.

Katmanlar arasında detached entity taşımak yerine açık bir DTO sözleşmesi kullanmak hem bu maliyeti hem de istemeden lazy yüklemeyi azaltır.

### Stateless yaklaşım

Çok büyük ve salt toplu veri akışında birinci düzey önbellek, cascade ve kirlilik denetimi istenmiyorsa durumsuz Hibernate oturumu veya doğrudan JDBC daha uygun olabilir. Daha az sihir, daha fazla açık sorumluluk demektir.

## 12. Open Session in View

Open Session in View açık olduğunda web katmanı persistence context'e erişmeye devam edebilir. Bu kolaylık:

- transaction sınırını görünmez yapar,
- JSON serileştirme sırasında lazy sorgu başlatabilir,
- N+1'i servis testlerinden saklayabilir,
- bağlantının beklenenden uzun tutulmasına yol açabilir.

Üretim veri erişiminde tercih edilen model:

```text
servis transaction'ı
-> gereken veriyi açıkça getir
-> DTO oluştur
-> transaction kapanır
-> web katmanı DB'ye dokunmaz
```

`open-in-view=false` tembel yükleme hatasını üretmiyorsa sistem düzgün demek değildir; hata çıkması daha önce gizlenen erişimin görünür olmasıdır.

## 13. Kimlik stratejileri

### Primary key'in görevi

Primary key yalnız hızlı arama aracı değildir; satır kimliğini ve veri modelindeki tekilliği tanımlar. ORM kimlik haritası, ilişki çözümü ve güncelleme semantiği bu kimliğe dayanır.

Mantıksal tekilliği ayrıca korumak gerekiyorsa benzersiz kısıt uygulanır. İş kuralı yalnız uygulama koduna bırakılırsa eşzamanlı iki istek kontrolü aynı anda geçebilir.

### IDENTITY

`IDENTITY` değeri ekleme sonrasında döndüğü için Hibernate'in insertleri geciktirip toplamasını zorlaştırır; yoğun batch yazmada uygun değildir.

### SEQUENCE

Sequence kimliği eklemeden önce alınabilir. Pooled veya pooled-lo artırıcı, her kimlik için ayrı veritabanı çağrısını azaltır.

Sequence cache büyütmek kimliklerde boşluk bırakabilir. Anahtarın görevi sıralı muhasebe numarası olmak değil kimlik sağlamaktır; boşluksuz sıra ayrı bir iş kuralıdır.

### UUID

Dağıtık üretim koordinasyon gerektirmez. Tam rastgele UUID, B-tree yaprak yerelliğini azaltabilir; zaman sıralı UUID biçimleri ekleme yerelliğini iyileştirir.

Anahtar seçimi:

```text
tek düğüm / DB sequence       sequence
dağıtık bağımsız üretim      UUID benzeri
iş açısından anlamlı anahtar natural key + ayrı teknik PK
```

olarak düşünülür.

## 14. Eşleme maliyeti

### Türler

Java türü ile veritabanı türü aynı anlamı taşımalıdır. Zaman dilimli ve zaman dilimsiz tarih-saat türlerini karıştırmak yalnız doğruluk değil indeks kullanımı ve dönüşüm maliyeti sorunu da üretir.

Enum sıra numarasıyla saklanırsa kaynak kodundaki sıra değişikliği eski verinin anlamını değiştirir. Kararlı metin veya kod değeri daha güvenlidir.

### Büyük alanlar

LOB, JSON ve büyük metin her listede taşınmamalıdır. Sıcak sorguda yalnız gereken sütunlar seçilir; büyük içerik ayrı erişim yoluna alınabilir.

"Lazy LOB" davranışı sağlayıcı ve bayt kodu iyileştirmesine bağlı olabileceği için yalnız anotasyona güvenilmez, üretilen SQL doğrulanır.

### Kalıtım

ORM kalıtımı nesne modelini veritabanına taşıdığı için sorgu maliyeti üretir.

- tek tablo en az join ile hızlıdır, alt sınıf kısıtları zayıflayabilir,
- joined bütünlüğü korur, her polimorfik okumada join maliyeti taşır,
- table-per-class polimorfik sorguda union üretir.

Polimorfik sorguya ihtiyaç yoksa veritabanında kalıtım kurmak zorunlu değildir.

## 15. İlişkiler ve N+1

### Varsayılan fetch'e güvenme

İlişkiyi `EAGER` yapmak N+1'i çözmez; pahalı yüklemeyi her sorguya zorlar. İlişkiler genellikle tembel tutulur ve gereken sorguda fetch planı açıkça seçilir.

### N+1

```text
1 kök sorgu
+ N ilişki sorgusu
= N+1
```

Her sorgu tek başına 1 ms olsa bile bin kayıt için ağ gidiş-dönüşü baskın hale gelir.

N+1'i bulmanın en güvenilir yolu istek başına sorgu sayısını veri hacmiyle birlikte izlemektir.

### Çözüm sırası

Salt okunur ekranda:

```text
DTO projection
```

varlık gerekiyorsa:

```text
JOIN FETCH / entity graph
```

ilişki yalnız bazı kayıtlarda gerekliyse:

```text
batch fetch
```

kullanılır.

Batch fetch, `N+1` davranışını yaklaşık `N/batch + 1` sorguya indirir; tek sorgu değildir fakat kartezyen büyümeyi önleyebilir.

### Koleksiyon fetch ve sayfalama

Koleksiyon join edildiğinde bir kök satır çok sayıda SQL satırına dönüşür. Aynı sorguda `LIMIT/OFFSET` uygulamak kök varlık sayısını değil join satırını sınırlar.

Güvenli iki aşamalı yol:

```text
1. sayfalanmış kök ID'lerini getir
2. bu ID'lerin ilişkilerini ayrı sorguda getir
```

olabilir.

### Çok koleksiyon

İki büyük koleksiyonu tek join ile çekmek:

```text
A satırı × B satırı
```

kadar ara sonuç üretir. Tek SQL her zaman az iş değildir.

## 16. Sonuç kümesi ve taşınan veri

### Projection

Liste ekranı beş alan gösteriyorsa elli sütunlu entity çekilmez. DTO, interface projection veya scalar sonuç kullanılır.

Veritabanı performansında temel ölçü:

```text
gereksiz satır + gereksiz sütun + gereksiz gidiş-dönüş
```

toplamıdır.

### Fetch size

Getirme boyutu sonuç kümesinin toplam büyüklüğünü değil sürücünün bir ağ turunda taşıdığı satır sayısını belirler:

```text
yaklaşık round-trip = satır / fetch size
```

Çok küçük değer ağ turunu, aşırı büyük değer istemci belleğini artırır. Sürücü davranışı doğrulanır.

### Stream

Çok büyük sonucu `List` içine almak yerine akışlı okumak belleği sınırlayabilir; fakat bağlantı stream kapanana kadar tutulur.

Kural:

```text
stream kısa işlem + düzenli tüketim + kesin close
```

olmalıdır.

Saatler süren yavaş tüketicinin connection'ı işgal etmesi, heap taşmasından farklı ama aynı derecede ciddi bir sorundur.

## 17. Sayfalama

### Offset

```sql
SELECT ...
ORDER BY created_at, id
OFFSET 100000 ROWS FETCH NEXT 50 ROWS ONLY;
```

derin sayfada atlanacak kayıtların maliyetini taşır.

### Anahtar tabanlı sayfalama

```sql
SELECT ...
WHERE (created_at, id) < (?, ?)
ORDER BY created_at DESC, id DESC
FETCH FIRST 50 ROWS ONLY;
```

uygun indeksle son görülen konumdan devam eder. Doğrudan "sayfa 5000" atlaması zorlaşır; sonsuz kaydırma, API taraması ve batch işlerinde bu genellikle kabul edilebilir.

Sıralama tekil olmalıdır. Yalnız `created_at` kullanmak aynı zamana sahip satırlarda atlama veya tekrar üretebilir; sonuna benzersiz anahtar eklenir.

### COUNT maliyeti

Her sayfada tam `COUNT(*)` gerekmez. Kullanıcı yalnız "sonraki var mı" diyorsa `Slice` benzeri yapı yeterlidir.

Toplam sayının iş gereksinimi olduğu yerde hesaplanır; framework varsayılanı olduğu için değil.

## 18. Toplu yazma

Batch'in kazancı CPU hilesi değil ağ gidiş-dönüşünü azaltmasıdır.

```text
10 000 insert
tek tek     -> 10 000 gönderim
batch 100   -> yaklaşık 100 grup
```

Gerçek paket sayısı sürücü ve veritabanı protokolüne bağlıdır.

Batch boyutu büyüdükçe:

- gidiş-dönüş azalır,
- istemci/sunucu tamponu büyür,
- hata anında geri alınacak iş büyür,
- tek kaydın bekleme süresi artabilir.

Bu nedenle 50 veya 100 gibi değerler yalnız ölçüm başlangıcıdır.

### Hibernate batch

Aynı SQL biçimindeki işlemler arka arkaya gelmelidir. Ekleme ve güncellemeleri sıralama, batch'in dolmasını kolaylaştırır.

Kimlik stratejisi batch'i desteklemelidir. `IDENTITY` ile ekleme sonrası kimlik gerektiğinden batch davranışı bozulabilir; sequence ve havuzlu artırıcı daha uygundur.

### Bulk DML

Tek SQL ile yapılan toplu güncelleme, bin entity yükleyip tek tek değiştirmekten çok daha ucuz olabilir:

```sql
UPDATE job
SET state = 'EXPIRED'
WHERE state = 'OPEN'
  AND expires_at < ?;
```

Ancak bulk DML persistence context'i dolaşır; context temizlenmeli veya etkilenen varlıklar yeniden yüklenmelidir.

## 19. Set tabanlı SQL ve satır satır işlem

Veritabanı küme işlemleri için tasarlanmıştır. PL/SQL veya uygulama kodunda satır satır döngü, aynı işi tek SQL'in yapabileceği durumda gidiş-dönüş, context switch ve kilit süresini artırır.

Sorun "cursor kullanmak" değildir; veritabanı zaten cursor kavramıyla çalışır. Sorun, küme tabanlı işi satır tabanlı algoritmaya çevirmektir.

Tercih sırası:

```text
tek set-based SQL
-> bulk/batch
-> zorunluysa kontrollü satır işleme
```

olmalıdır.

RAC gibi paylaşımlı veritabanı kümelerinde gereksiz satır gezme yalnız CPU değil düğümler arası blok koordinasyonunu da büyütebilir.

## 20. SQL hazırlama ve plan önbelleği

SQL'in yaşamı:

```text
parse
-> anlam çözümleme
-> plan seçimi
-> yürütme
-> sonuç
```

Kısa OLTP sorgusunda hard parse maliyeti sorgunun kendisine yaklaşabilir.

### Bind parametreleri

```sql
WHERE user_id = ?
```

aynı SQL metninin tekrar kullanılmasını sağlar ve SQL enjeksiyonuna karşı temel savunmadır.

Değerleri metne birleştirmek:

```text
farklı SQL metni
-> daha çok parse
-> plan cache parçalanması
-> güvenlik riski
```

üretir.

### Veri dağılımı

Aynı plan her bind değeri için en iyi olmayabilir. Çok çarpık dağılımlı sütunda bir değer tek satır, başka bir değer tablonun yarısını döndürebilir.

Plan sorunu görülmeden "bind kötü" denmez. Gerçek satır tahmini, istatistik ve hedef veritabanının adaptif plan mekanizmaları incelenir.

### IN listeleri

Değişken uzunluklu `IN` listeleri farklı SQL şekilleri üreterek plan önbelleğini parçalayabilir. ORM'in parametre padding özelliği bazı iş yüklerinde SQL biçimi sayısını azaltır; her sorguda açılacak evrensel hız anahtarı değildir.


### Transaction pooler ve hazırlanmış ifadeler

PostgreSQL önünde PgBouncer gibi bir pooler kullanılıyorsa uygulama havuzu ile sunucu oturumu aynı kavram olmaktan çıkar. Session pooling sunucu bağlantısını istemci oturumu boyunca tutar. Transaction pooling her transaction sonunda sunucu bağlantısını havuza geri verir; statement pooling ise çok ifadeli transaction'ı bile engeller.

Transaction pooling bağlantı sayısını güçlü biçimde azaltabilir fakat session durumuna dayanan özellikler ayrıca incelenir. Güncel PgBouncer sürümleri protokol düzeyindeki named prepared statement'ları `max_prepared_statements` ile transaction/statement pooling altında takip edebilir; buna rağmen `SET/RESET`, session advisory lock, belirli temporary table davranışları ve SQL `PREPARE` gibi session semantiği aynı değildir.

Buradaki Zen kuralı basittir: oturuma ihtiyacınız yoksa oturum durumu üretmeyin. Uygulama her istekte bilinmeyen bir önceki oturum durumuna güveniyorsa havuzlama katmanı deterministik değildir.

## 21. Yürütme planı

Plan, sorgunun nasıl çalışacağını gösteren ağaçtır. İlk bakılacak yer "indeks kullandı mı" değil:

```text
tahmini satır
gerçek satır
```

farkıdır.

Optimizer 100 satır bekleyip 10 milyon satır görürse join türü, erişim yolu ve bellek tahsisi yanlış olabilir.

### Erişim yolları

**Tam tarama:** Küçük tablo veya çok satır dönen sorguda doğru olabilir.

**İndeks taraması:** Seçicilik yüksekse az sayfa okur; çok kayıt döndürürse tablodaki rastgele erişim pahalılaşır.

**Yalnız indeks:** Gereken sütunların tamamı indekste ise tabloya dönüş ortadan kalkar.

### Join algoritmaları

**Nested loop:** Dış küme küçük, iç tarafta uygun indeks varsa güçlüdür.

**Hash join:** Büyük eşitlik birleşimlerinde etkilidir; hash tablosu belleğe sığmazsa diske taşar.

**Merge join:** Girdiler sıralıysa ucuzdur; sıralama gerekiyorsa maliyet önce ödenir.

Plan okurken yalnız toplam süre değil blok okuma, spill, tekrar sayısı ve gerçek satır akışı incelenir.


### EXPLAIN ile gerçek yürütmeyi ayırmak

`EXPLAIN` optimizer tahminini, `EXPLAIN ANALYZE` ise sorguyu gerçekten çalıştırarak gözlenen süre ve satır akışını verir. Üretimde yan etkili DML için bu ayrım kritiktir; "plan bakıyorum" diye veri değiştiren komutu çalıştırmak kabul edilemez.

PostgreSQL'de `BUFFERS` gibi seçenekler cache hit, okuma ve yazma davranışını görünür kılar. Oracle tarafında gerçek yürütme istatistikleri ve satır sayıları benzer amacı taşır. Ürün farklı, soru aynıdır:

```text
optimizer ne bekledi?
gerçekte ne oldu?
kaç blok/sayfa taşındı?
spill oldu mu?
aynı düğüm kaç kez çalıştı?
```

Plan maliyet sayıları veritabanları arasında karşılaştırılacak milisaniye değildir. Aynı optimizer içindeki alternatifleri sıralayan göreli modeldir.

## 22. İndeksleme

İndeksleme çalışmalarından çıkan temel ders şudur: indeks, yalnız performans eklentisi değil veri erişim niyetinin fiziksel ifadesidir; primary ve unique kısıtlar ise önce doğruluğu korur.

### Primary ve unique

Primary key satırın tekil kimliğidir. Unique constraint, "aynı iş anahtarı iki kez oluşamaz" kuralını eşzamanlı isteklerde de korur.

Uygulamada:

```text
önce SELECT var mı?
sonra INSERT
```

yaklaşımı yarış koşuluna açıktır. Benzersiz kısıt aynı kuralı atomik olarak veritabanında uygular.

### B-tree

B-tree:

- eşitlik,
- aralık,
- sıralama,
- önek

erişiminde genel amaçlı varsayılan yapıdır.

Arama maliyeti teorik olarak logaritmik olsa da gerçek süre dallanma faktörü, önbellek, satır erişimi ve seçicilikten etkilenir.

### Bileşik indeks

```text
(a, b, c)
```

indeksi soldan önek kuralına göre çalışır.

Genel sıra:

```text
eşitlik sütunları
-> aralık sütunu
-> sıralama / kapsama sütunları
```

şeklinde düşünülür; gerçek planla doğrulanır.

### Kapsayıcı indeks

Sorgunun gereken bütün alanlarını indeks taşıyorsa tabloya geri dönme gerekmeyebilir. Okuma azalır; indeks büyür ve yazma pahalılaşır.

### Kısmi / filtreli indeks

Yalnız sıcak alt kümeyi indekslemek:

```text
WHERE active = true
```

gibi sorgularda indeks boyutunu dramatik azaltabilir. Veritabanı desteği ve sözdizimi ürüne bağlıdır.

### İfade indeksi

```sql
WHERE LOWER(code) = ?
```

koşulunda normal `code` indeksi her veritabanında kullanılamayabilir; sorgudaki ifadeye uygun function-based/expression index gerekebilir.

### Yabancı anahtar

Yabancı anahtar sütunları sık join ve üst satır silme/güncelleme davranışında önemlidir. İndeks eksikliği bazı veritabanlarında geniş tarama ve kilit etkisi üretir.

### İndeks maliyeti

Her indeks:

```text
INSERT
UPDATE(indexed column)
DELETE
```

sırasında bakım ister; WAL/redo hacmini, depolama alanını ve cache tüketimini artırır.

Bu yüzden:

```text
indeks yok
```

kadar:

```text
her sütunda indeks
```

de hatalıdır.

Kullanılmayan ve birbirini kapsayan indeksler düzenli ölçülür.

### Tam tarama her zaman hata değildir

Düşük seçicilikli sorguda tablonun büyük bölümü zaten okunacaksa indeks:

```text
indeks oku
-> rowid/tuple konumu bul
-> tabloya rastgele git
```

maliyeti yüzünden tam taramadan yavaş olabilir.

Doğru soru "indeks neden kullanılmadı" değil "bu sorgu için en az iş hangi erişim yolunda"dır.


### Hash, GIN ve BRIN

B-tree genel varsayılandır, tek indeks türü değildir.

**Hash indeks** eşitlik erişimine özeldir. Aralık ve sıralama sağlamaz; B-tree'nin zaten iyi olduğu sıradan eşitlik sorgularında otomatik üstün kabul edilmez.

**GIN** ters indeks mantığıyla bir satır içinde çok sayıda anahtarın bulunduğu yapılarda güçlüdür. PostgreSQL'de array, tam metin ve uygun `jsonb` operatörleri bunun tipik örnekleridir. Okumayı hızlandırırken güncelleme ve indeks bakım maliyeti büyüyebilir.

**BRIN** her satırı ayrı indekslemek yerine fiziksel blok aralıkları için özet tutar. Çok büyük ve fiziksel sıralamayla güçlü korelasyonu olan zaman/artan kimlik verisinde çok küçük indeksle geniş aralıkları eleyebilir. Veri korelasyonu kötüyse kazanç azalır.

İndeks türü veri tipinden değil operatörden ve erişim deseninden seçilir.

### PostgreSQL'te bellek ve bakım

PostgreSQL'de `shared_buffers`, `work_mem` ve `effective_cache_size` aynı bellek değildir. `shared_buffers` için dokümantasyondaki yüzde 25 değeri yalnız dedicated sunucuda başlangıç noktasıdır; performans formülü değildir. `work_mem` ise bağlantı başına tek sabit alan gibi düşünülmemelidir. Bir sorgu birden çok sort/hash operasyonu, bir sistem birçok eşzamanlı sorgu ve parallel worker çalıştırabilir; toplam bellek ayarın katlarına çıkabilir.

Autovacuum yalnız "silinen satırı temizleme" işi değildir. MVCC dead tuple geri kazanımı, planner istatistiği ve visibility map bakımı üzerinden index-only scan davranışına kadar okuma performansını etkiler. Autovacuum'u performans için kapatmak kısa benchmark'ı güzelleştirip uzun vadeli sistemi bozabilir.

`pg_stat_statements`, en pahalı tek sorguyu değil toplam sistem yükünü oluşturan SQL ailelerini görmek için kullanılır. Çağrı sayısı, toplam süre, ortalama süre ve plan/IO verileri birlikte okunur.

Read replica, `@Transactional(readOnly=true)` benzeri uygulama sinyaliyle yönlendirilebilir; ancak replica lag ve read-your-writes semantiği ayrıca çözülmeden yalnız routing performans optimizasyonu değildir.

## 23. SSD, disk ve yazma büyütmesi

İndekssiz sorgunun temel maliyeti gereksiz okuma, CPU, tampon ve eşzamanlı kaynak tüketimidir. SSD ömrünü esas belirleyen mekanizma ise okuma sayısından çok yazılan veri miktarı ve yazma büyütmesidir.

Yazma büyütmesi:

```text
fiziksel yazılan bayt
--------------------- 
mantıksal uygulama yazısı
```

oranıdır.

B-tree yazısı WAL ve veri sayfasına, LSM yazısı WAL, memtable flush ve compaction aşamalarına gidebilir. Her iki yapı da bir mantıksal yazıyı birden çok fiziksel yazıya dönüştürür.

Random write flash garbage collection yükünü artırabilir; sıralı yazı depolamanın iç yerelliğinden daha iyi yararlanır.

Performans testi kısa tutulursa LSM yapıda compaction henüz başlamadan ölçüm bitebilir ve gerçek sürdürülebilir yazma hızı olduğundan yüksek görünür. Yük testi kararlı duruma kadar sürmelidir.

## 24. B-tree ve LSM

B-tree sayfaları yerinde günceller. Nokta ve aralık okumasında az ve öngörülebilir sayıda sayfa erişimi sağlar.

LSM yaklaşımı:

```text
WAL
-> memtable
-> SSTable
-> compaction
```

zinciriyle yazıyı sıralı ve ardışık hale getirir. Okuma birden çok SSTable'a bakabilir; Bloom filtreleri olmayan anahtarlar için gereksiz disk erişimini azaltır.

Genel eğilim:

```text
B-tree    okuma gecikmesi / aralık sorgusu avantajı
LSM       yüksek yazma verimi avantajı
```

şeklindedir; gerçek seçim veri büyüklüğü, anahtar dağılımı, overwrite oranı, compaction stratejisi ve depolamaya göre ölçülür.

Compaction geri planda çalışan "bedava" iş değildir. Yazma hızı compaction kapasitesini geçerse sistem geri basınç uygulamak zorunda kalır.

## 25. OLTP ve OLAP

Operasyonel sistem veri üretir; analitik sistem büyük veri kümelerini okuyup toplulaştırır.

```text
OLTP                         OLAP
-------------------------    --------------------------
kısa işlem                   uzun tarama / agregasyon
az satır                     çok satır
sık yazma                    çoğunlukla okuma
satır odaklı                 sütun odaklı
indeksli nokta erişimi       tarama, sıkıştırma, vektörleme
düşük gecikme                yüksek toplam verim
```

Aynı fiziksel sistem iki yükü de taşıyabilir fakat biri diğerinin gecikmesini bozuyorsa iş yükleri ayrılmalıdır.

### Satır ve sütun depolama

OLTP'de bir kaydın alanlarını birlikte tutmak tek satır okumayı ucuzlatır. Analitik sorgu yüz sütundan üçünü okuyorsa satır depolama gereksiz 97 sütunu da taşır.

Sütun depolama yalnız gereken sütunları okur, benzer değerleri iyi sıkıştırır ve SIMD/vektörleştirme ile büyük blokları düşük CPU maliyetiyle işler.

### Materialized view

Sık tekrarlanan pahalı okuma sonucu önceden hesaplanabilir. Bu, okuma maliyetini yazma/yenileme maliyetine taşır.

Materialized view kaynak gerçek değildir; türetilmiş veridir. Kaynak kaybolmadan yeniden üretilebiliyorsa mimari daha güvenlidir.

### OLTP'den analitiğe veri akışı

Analitik sorguları doğrudan işlem veritabanında çalıştırmak yerine:

```text
OLTP
-> CDC / ETL
-> analitik sistem
```

yaklaşımı işlem yolunu korur.

ClickHouse gibi sütunlu sistemler Oracle veya PostgreSQL'in "yerine geçen" genel OLTP motoru olarak değil, farklı iş yükü için ayrı araç olarak düşünülür.

## 26. Sistem kaydı ve türetilmiş veri

Sistem kaydı (system of record) bir gerçeğin yetkili kopyasını tutar. Önbellek, arama indeksi, materialized view, veri ambarı ve makine öğrenmesi modeli türetilmiş veridir.

```text
kaynak gerçek
-> dönüşüm
-> türetilmiş görünüm
```

Türetilmiş veri kaybolduğunda kaynaktan yeniden üretilebiliyorsa hata yönetimi basitleşir.

Bu ayrım önbellek tartışmasını da sadeleştirir: önbellek kaynak gerçek değilse kaybı veri kaybı değildir, yalnız performans kaybıdır.

## 27. Çoğaltma

### Neden çoğaltılır

Çoğaltma üç amaç taşır:

- arızaya dayanım,
- okuma kapasitesi,
- kullanıcıya yakın veri.

Aynı mekanizma üç amacı aynı maliyetle çözmez.

### Fiziksel ve mantıksal çoğaltma

Fiziksel çoğaltma WAL/depolama değişikliklerini düşük seviyede taşır. Hızlı ve storage motoruna yakındır; sürüm uyumluluğunu zorlaştırabilir.

Mantıksal çoğaltma tablo satırı düzeyinde değişiklikleri taşır. Dış sistemlerin okuyabilmesi ve CDC için daha uygundur.

Primary key eksikliği mantıksal değişiklik kaydını da pahalılaştırabilir; güncellenen/silinen satırı tanımlamak için daha çok eski veri taşımak gerekir.

### Eşzamanlı ve eşzamansız

Eşzamanlı kopya commit yoluna ek dayanıklılık, gecikme ve arıza bağımlılığı getirir. Eşzamansız kopya yazı yolunu hızlandırır fakat lider kaybında henüz çoğaltılmamış yazılar kaybolabilir.

Doğru seçim RPO ve gecikme hedefinden çıkar.

### Kopya gecikmesi

Okumayı replica'ya taşımak birincil düğümü rahatlatır, fakat veri eski olabilir.

Özellikle:

```text
yaz
hemen oku
```

akışında kendi yazdığını görememe kullanıcı hatası gibi görünür.

Yöntemler:

- yazı sonrası belirli süre primary'den oku,
- kullanıcının son yazı konumunu izleyip replica yetişene kadar primary kullan,
- güçlü tutarlılık gereken uç noktayı replica'ya yönlendirme.

Replica lag bir sayı olarak izlenmelidir; "eventual" gözlenemeyen bir garanti olmamalıdır.

## 28. CDC

Change Data Capture, değişikliği sorguyla tekrar tekrar aramak yerine veritabanı günlük akışından alır.

```text
transaction DB
-> değişiklik günlüğü
-> CDC
-> kuyruk / arama / analitik / cache
```

Bu yaklaşım:

- OLTP'yi rapor sorgularından ayırır,
- yakın gerçek zamanlı veri akışı sağlar,
- türetilmiş sistemleri kaynak DB'den bağımsızlaştırır.

Oracle GoldenGate ve Debezium farklı işletim, lisans ve entegrasyon özelliklerine sahip örneklerdir; seçim "hangisi daha hızlı" sorusundan değil kaynak sistem, kapalı ağ, destek, operasyon ve hata geri kazanımı gereksinimlerinden yapılır.

CDC de tam olarak bir kez yan etki garantisi vermez. Konum, işlem kimliği ve tüketici idempotency'si tasarımın parçasıdır.

## 29. Bölümleme

Bölümleme tek büyük tabloyu fiziksel parçalara ayırır. Zaman eksenli veride:

```text
2026-07
2026-08
2026-09
```

gibi bölümler sorgu budamasını ve eski veriyi hızlı düşürmeyi sağlar.

Eski milyarlarca satırı `DELETE` etmek yerine bölüm düşürmek:

- undo/redo üretimini,
- satır kilidini,
- indeks bakımını

büyük ölçüde azaltabilir.

Bölüm anahtarı sorgu koşulunda yoksa bütün bölümler taranır ve mimari kazanç kaybolur.

### Yerel ve global indeks

Yerel indeks bölümle birlikte yönetilir; bölüm bakımında daha bağımsızdır. Global indeks bütün tabloyu tek erişim yapısında görür ve bölüm sınırı dışındaki sorguları kolaylaştırabilir; bölüm bakımı daha pahalı ve karmaşık hale gelir.

"Her zaman local" veya "her zaman global" kuralı yoktur; erişim yolu ve bakım şekli belirler.

## 30. Sharding

Bölümleme bir veritabanı içinde yapılabilir; sharding veriyi birden çok düğüm arasında dağıtır.

### Aralık ve hash

Aralık bölme range sorgularını korur fakat artan anahtar tek sıcak shard oluşturabilir.

Hash dağıtımı yükü daha dengeli yayar fakat anahtar aralığı sorgusunu bütün shard'lara saçabilir.

Birleşik anahtar:

```text
partition key + sort key
```

ile aynı partition içindeki aralık sorguları korunabilir.

### Sıcak anahtar

Tutarlı hashing anahtarları eşit dağıtsa bile istekleri eşit dağıtmaz. Çok popüler tek kullanıcı veya kayıt, kendi shard'ını doyurabilir.

Çözüm veri dağıtımından önce yük dağılımını ölçmektir. Gerekirse sıcak anahtar alt parçalara ayrılır, okuma cache'e alınır veya yazılar birleştirilir.

### İkincil indeks

Yerel ikincil indeks yazıda tek shard'ı etkiler fakat arama tüm shard'lara yayılabilir. Global ikincil indeks aramayı daraltır fakat yazı birden çok shard'da indeks güncellemesi gerektirir.

Yine aynı değiş tokuş vardır:

```text
ucuz write -> pahalı read
pahalı write -> ucuz read
```

### Dağıtık işlem

Bir iş iki shard'a yazıyorsa tek düğümlü transaction varsayımı biter. Dağıtık transaction, saga veya iş kuralını aynı shard'a yerleştirecek model seçimi değerlendirilir.

En ucuz dağıtık transaction, hiç dağıtılmayan transaction'dır.

## 31. Veri modeli ve erişim yolu

Normalizasyon yinelenen gerçeği tek yerde tutarak yazma tutarlılığını kolaylaştırır. Denormalizasyon join maliyetini azaltır fakat aynı gerçeğin birden çok kopyasını senkron tutma sorumluluğu getirir.

Karar:

```text
kaynak gerçek normalize
okuma görünümü gerekirse türet
```

yaklaşımıyla sadeleşir.

Denormalize alan güncellenecekse güncelleme mekanizması açık olmalıdır: aynı transaction, CDC, olay işleme veya yeniden hesaplama.

"Performans için denormalize ettik" tek başına yeterli tasarım gerekçesi değildir; hangi okumanın kaç kat ucuzladığı ve tutarlılığın nasıl korunduğu ölçülmelidir.

## 32. Event sourcing ve CQRS

Olay kaynaklı modelde değişiklik önce değişmez bir olay olarak saklanır, okuma görünümleri bu olaylardan türetilir:

```text
komut
-> doğrulama
-> olay
-> materialized view'lar
```

Avantajları:

- olayın nedenini açıkça taşır,
- denetim izi sağlar,
- farklı okuma modelleri yeniden üretilebilir,
- yeni görünüm geçmiş olaylardan kurulabilir.

Bedelleri:

- olay sırası ve şema evrimi,
- yeniden oynatmada deterministik davranış,
- dış yan etkilerin tekrar edilmemesi,
- kişisel verinin silinmesi,
- operasyonel karmaşıklık.

Bu nedenle CQRS veya event sourcing sıradan CRUD uygulamasının varsayılanı değildir. Audit, yeniden oynatma ve çok farklı okuma modelleri gerçek gereksinimse kullanılır.

Yeniden oynatılan olay dış döviz kuru, güncel saat veya değişken uzak servis sonucuna bağlıysa aynı olay farklı sonuç üretir. Gerekli dış veri olayın içinde veya tarihsel olarak tekrar sorgulanabilir biçimde saklanmalıdır.

## 33. Şema evrimi

Üretimde eski ve yeni uygulama sürümleri aynı anda çalışabilir. Şema değişikliği tek deploy anı değil geçiş dönemidir.

Güvenli değişim:

```text
1. yeni alanı ekle
2. eski kod çalışmaya devam etsin
3. yeni kod iki biçimi de okusun
4. veri gerekiyorsa arka planda doldur
5. tüm okuyucular geçince eski alanı bırak
6. son olarak eski yapıyı kaldır
```

Bu expand-contract yaklaşımı kesintisiz değişimin temelidir.

API ve mesaj şemalarında da aynı kural geçerlidir: yeni okuyucu eski veriyi, eski okuyucu yeni veriyi mümkün olduğunca anlayabilmelidir.

Şema tabanlı ikili biçimler alan numarası ve varsayılan değerler üzerinden ileri/geri uyumluluk sağlayabilir; ancak uyumluluk kuralı yalnız biçim değil semantik için de korunmalıdır. "Alan artık metre değil santimetre" gibi anlam değişikliği tür aynı kaldığı halde uyumsuzdur.

## 34. Önbellek

### Önce kaynağı düzelt

Önbellek kötü sorgunun yerine geçmez. İndeks eksikliği cache ile gizlenirse:

```text
cache miss
restart
toplu expiry
```

anında aynı sorun geri gelir.

### Katmanlar

- persistence context: transaction içi kimlik haritası,
- ORM ikinci düzey önbelleği: oturumlar arası entity,
- uygulama önbelleği: iş sonucu,
- uzak paylaşımlı önbellek: örnekler arası veri,
- HTTP/CDN önbelleği: istemciye yakın sonuç.

Her katmanın geçersizleştirme sınırı farklıdır.

### İkinci düzey ORM cache

Sık okunan, seyrek değişen referans verisinde değerlidir. Yoğun yazılan işlem tablosunda geçersizleştirme ve koordinasyon maliyeti kazancı yok edebilir.

Native SQL, trigger veya başka uygulama aynı tabloyu değiştiriyorsa ORM cache bu değişikliği otomatik bilmeyebilir.

### Query cache

Sorgu sonucu yerine çoğu ORM kimlik listesini saklar. Entity cache ile birlikte düşünülmezse isabet durumunda bile ek yüklemeler üretebilir.

Yazma yoğun tabloda her değişiklik sorgu cache girdilerini geçersiz kılıyorsa hit ratio tek başına anlamsızdır.

### Stampede

Popüler anahtar aynı anda expire olursa yüzlerce istek kaynağa koşar.

Çözümler:

- single-flight,
- TTL jitter,
- stale-while-revalidate,
- önden yenileme,
- gerektiğinde giriş hızı sınırı.

### Negatif cache

"Bulunamadı" sonucu pahalıysa kısa süre önbelleklenebilir. Süre uzun tutulursa yeni oluşturulan veri görünmez; negatif cache'in TTL'i iş semantiğine göre kısa seçilir.


### Caffeine: kabul politikası ve yenileme

Yerel cache boyutu yalnız kayıt sayısıyla belirlenmez. Değerlerin maliyeti çok farklıysa ağırlıklandırma gerekir. Caffeine'in Window TinyLFU politikası recency ile frequency bilgisini birleştirerek tarama gibi LRU'yu kirleten iş yüklerinde daha iyi kabul/çıkarma kararı vermeyi amaçlar.

`expireAfterWrite` ile `refreshAfterWrite` aynı davranış değildir. Expire edilen kayıtta yeni istek yeniden yüklemeyi bekleyebilir. Refresh'e uygun kayıt ise ilk stale erişimde asenkron yenilenirken eski değer dönmeye devam edebilir. Bu, P99'u korumak için yararlıdır; fakat eski verinin ne kadar kabul edilebilir olduğu iş kuralıdır.

Cache metriklerinde yalnız hit ratio yetmez:

```text
hit rate
miss sayısı
miss yükleme maliyeti
load failure
load P99
eviction sayısı / ağırlığı
cache boyutu
```

birlikte izlenir. Yüzde 99 hit rate, kalan yüzde 1 miss bütün veritabanını doyuruyorsa iyi değildir.

### Redis: pipelining atomiklik değildir

Redis pipelining birden çok komutu cevap beklemeden göndererek RTT ve socket syscall maliyetini azaltır. Binlerce komutu tek dev pipeline'a koymak ise sunucuda cevapların kuyruklanacağı belleği büyütür; sınırlı batch'ler kullanılır.

Pipelining, `MULTI/EXEC` ile aynı şey değildir. Pipeline ağ optimizasyonudur; transaction komut grubunun yürütme semantiğini değiştirir. Atomiklik gereksinimi yokken transaction kullanmak, yalnız round-trip azaltmak için doğru araç değildir.

İki katmanlı cache:

```text
L1 Caffeine
-> L2 Redis
-> DB
```

okuma gecikmesini azaltabilir fakat geçersizleştirme artık üç katmanlıdır. L1'in çok uzun yaşaması Redis güncelliğini anlamsızlaştırır. Sürüm numarası, olay tabanlı invalidation veya kısa TTL gibi politika açık tanımlanmalıdır.

## 35. Mesaj kuyrukları

Kuyruk yükü yok etmez, zaman içinde yeniden dağıtır.

Üretici batch boyutu ve bekleme süresi arasında seçim yapar:

```text
büyük batch -> daha yüksek verim, daha fazla gecikme
küçük batch -> daha düşük gecikme, daha fazla protokol maliyeti
```

Bölüm sayısı tüketici paralelliğini artırır fakat sıra garantisi bölüm içindedir.

Tüketici gecikmesi (lag) yalnız kuyruk metriği değildir. Kesinti sonrası biriken milyonlarca mesaj hızla tüketilirse arka uç veritabanı normal trafiğin katlarıyla vurulabilir.

Geri kazanım:

```text
kademeli tüketim
hız sınırı
arka uç doygunluğuna göre backpressure
```

ile yapılır.

İşleme en az bir kez gerçekleşebiliyorsa tüketici aynı mesajı tekrar güvenle işleyebilmelidir.


### Kafka üretici yolu

Kafka producer aynı partition'a giden kayıtları batch'leyerek protokol maliyetini azaltır. `batch.size` üst sınırı, `linger.ms` ise batch dolmadan önce beklenebilecek süreyi etkiler. Kafka 4.0 ile `linger.ms` varsayılanının 0'dan 5 ms'ye değişmesi önemli bir ders taşır: çok küçük kontrollü bekleme, daha dolu batch nedeniyle hem verimi artırıp hem efektif gecikmeyi düşürebilir. Varsayılan yine de SLO değildir; hedef iş yükünde ölçülür.

Sıkıştırma da aynı değiş tokuştur:

```text
daha az ağ ve broker I/O
karşılığında
producer/broker/consumer CPU
```

`acks=all` ve idempotent producer dayanıklılık ve duplicate kontrolünü güçlendirir. Idempotence, producer retry'larından doğan stream içi kopyaları önler; dış veritabanı, HTTP çağrısı veya tüketici yan etkisi için uçtan uca exactly-once garantisi değildir.

Partition sayısı paralellik kadar sıra sınırıdır. Aynı iş anahtarının sırası önemliyse partition key bunu korumalıdır. Daha çok partition broker metadata, açık dosya, rebalance ve operasyon maliyeti de getirir.

### Kafka tüketici yolu

Tüketicide önemli soru "kaç thread" değil bir poll ile alınan işin deadline içinde bitip bitmediğidir. `max.poll.records`, fetch boyutları ve `max.poll.interval.ms` birlikte değerlendirilir. Çok kayıt almak fetch verimini artırırken işleme süresi poll aralığını aşarsa group rebalance oluşabilir.

Offset commit, işin gerçekten tamamlandığı noktayla uyumlu olmalıdır. Mesaj işlenmeden offset kalıcılaşırsa kayıp; işlendikten sonra crash olup offset yazılamazsa tekrar doğar. Bu nedenle en az bir kez teslimde idempotent tüketici temel tasarımdır.

Consumer lag için tek eşik yeterli değildir. Şunlar birlikte izlenir:

```text
lag records
lag time
tüketim rate
üretim rate
rebalance
processing P99
arka uç saturation
```

Kesinti sonrası replay, normal trafikten daha tehlikeli olabilir. Consumer'ın teorik maksimum hızına çıkmak yerine veritabanı, uzak servis veya dosya sisteminin güvenli geri kazanım kapasitesine göre hız sınırı uygulanır.

## 36. Geri basınç ve aşırı yük

Sağlıklı sistem kapasite sınırını gizlemez.

Aşırı yükte tercih sırası:

```text
sınırsız kuyruk
değil

sınırlı kuyruk
-> timeout
-> rate limit
-> load shedding
```

olmalıdır.

Sınırsız kuyruk, "başarılı kabul edilen ama dakikalar sonra işlenen" istekler üretir; bellek taşması veya timeout zinciri kaçınılmaz hale gelir.

### Timeout bütçesi

Bir üst isteğin 500 ms bütçesi varsa üç alt servise ayrı ayrı 500 ms vermek bütçe değildir.

```text
toplam bütçe
-> bağlantı
-> kuyruk
-> sorgu
-> alt servis
```

olarak bölünür.

Timeout, iş tamamlanmadıktan sonra kaynağın da bırakılmasını sağlamalıdır. İstemci vazgeçtiği halde veritabanında sorgu dakikalarca çalışıyorsa yük azalmamıştır.

### Yeniden deneme fırtınası

Bir servis yavaşladığında bütün üst katmanlar aynı isteği yeniden denerse gerçek trafik katlanır.

```text
1 kullanıcı isteği
× 3 gateway retry
× 3 servis retry
× 3 DB retry
= 27 girişim
```

gibi büyüme mümkündür.

Retry tek bir katmanda sahiplenilir, geçici hata sınıflandırılır, toplam süre bütçesi korunur ve jitter kullanılır.

## 37. Oracle RAC bağlamında erişim tasarımı

RAC aynı veriyi birden çok düğümden erişilebilir kılar; ortak veri blokları üzerindeki koordinasyon maliyetini ortadan kaldırmaz.

Aşağıdaki kalıplar küme maliyetini büyütebilir:

- aynı sıcak satıra yoğun yazma,
- düşük seçicilikli geniş tarama,
- gereksiz indeks bakımı,
- uzun transaction,
- satır satır işlem,
- düğümler arasında sürekli taşınan sıcak bloklar.

Sorun "RAC yavaş" değildir. Yerel tek düğümde görünmeyen koordinasyon maliyeti ölçek büyüdüğünde görünür olur.

RAC'e yeni düğüm eklemek uygulama veri modelindeki serileşme noktasını çözmez. Aynı anahtar üzerinde tekil kilit varsa fiziksel düğüm sayısı artarken iş hâlâ o kilitte sıraya girer.

## 38. OLTP ve analitik yükü ayırma

Kurumsal sistemde ana işlem veritabanı:

- kısa,
- seçici,
- transaction gerektiren,
- doğruluk hassas

işler için korunmalıdır.

Rapor, geçmiş taraması, model eğitimi, zaman serisi agregasyonu ve büyük dışa aktarma:

- replica,
- veri ambarı,
- sütunlu OLAP,
- dosya/lakehouse

katmanına taşınabilir.

CDC, bu ayrımı sorgu tabanlı kopyalamadan daha doğal yapar. Kaynağı her dakika `SELECT WHERE modified_at > ?` ile taramak hem indeks hem zaman damgası doğruluğu hem silinen kayıtlar açısından kırılgandır.

## 39. Arama ve vektör indeksleri

B-tree her sorgunun cevabı değildir.

Tam metin araması ters indeks, coğrafi sorgular çok boyutlu indeks, benzerlik araması vektör indeksi gerektirir.

AI uygulamalarında HNSW ve IVF gibi yaklaşık en yakın komşu yapıları:

```text
gecikme
bellek
indeks oluşturma maliyeti
recall
güncelleme maliyeti
```

arasında seçim yapar.

Yaklaşık vektör indeksi primary key veya transaction indeksinin yerine geçmez; farklı sorgu problemidir. Operasyonel metadata ilişkisel, semantik arama vektör indeksli ayrı türetilmiş görünüm olabilir.

Kaynak gerçek ile arama indeksinin ayrılması, indeks bozulduğunda kaynaktan yeniden üretmeyi mümkün kılar.

## 40. JVM belleği ve GC

### Önce allocation

Yüksek allocation:

```text
daha sık GC
daha çok bellek bant genişliği
daha fazla cache baskısı
```

demektir.

Sıcak yollarda en sık kaynaklar:

- büyük DTO grafikleri,
- gereksiz ara koleksiyon,
- String birleştirme,
- JSON serileştirme,
- boxing,
- sonuç kümesini bütünüyle belleğe alma.

Yığın büyütmek allocation sorununu çözmez, yalnız bir süre erteler.

### G1, ZGC ve Parallel

Toplayıcı seçimi:

```text
verim
gecikme
bellek ayak izi
```

üçlüsünde yapılır.

G1 genel amaçlı dengeli seçimdir. ZGC düşük duraklama hedefli işlerde anlamlıdır. Parallel GC toplu işte toplam verim önceliği varsa güçlü olabilir.

Sürüm, heap ve iş yükü doğrulanmadan "en hızlı GC" diye seçim yapılmaz.

### Yığın dışı bellek

Süreç belleği:

```text
heap
+ metaspace
+ thread stacks
+ direct buffers
+ code cache
+ native libraries
+ GC structures
```

toplamıdır.

Konteyner sınırının tamamını `-Xmx` yapmak OOMKill riskini artırır.

### Isınma

JIT derleme nedeniyle kısa benchmark kararlı durum değildir. Test:

```text
warm-up
-> kararlı yük
-> ölçüm
```

olarak ayrılır.

Native image başlangıç ve ayak izi kazandırabilir; uzun ömürlü yüksek verim servisinde JIT tepe performansı daha iyi olabilir. Karar başlangıç SLO'su ve ölçümle verilir.


### G1 ayrıntısı

G1, heap'i region'lara böler ve genç/yaşlı nesneleri aynı bölgesel yapı üzerinde yönetir. Büyük nesneler humongous allocation olarak özel davranış gösterebilir; region boyutunun yarısını aşan nesneler GC yerleşimini ve boş alan kullanımını bozabilir. Çözüm region flag'ini körlemesine değiştirmek değil hangi allocation'ın bu nesneyi ürettiğini bulmaktır.

Duraklama hedefi garanti değildir. GC, mevcut heap, allocation rate ve canlı veri oranı içinde hedefe yaklaşmaya çalışır. Heap çok sıkıysa collector'ın manevra alanı kalmaz; gereğinden büyükse RSS ve cache ayak izi büyür.

### ZGC'nin güncel durumu

JDK 23 ile generational ZGC varsayılan moda geçti; JDK 24 ve sonrasında ZGC generational modeldir ve ayrı `ZGenerational` seçeneğine ihtiyaç kalmamıştır. Eski "non-generational ZGC mi generational mı" tuning notları güncel JDK için tarihsel kabul edilmelidir.

ZGC'nin temel amacı çok düşük pause süreleridir; ağır işi büyük ölçüde eşzamanlı yürütür. Bu bedelsiz değildir: CPU ve bellek headroom gerekir. Batch verimi tek hedefse Parallel GC; genel amaçlı denge gerekiyorsa G1; sıkı pause bütçesi varsa ZGC adaydır. Aday, sonuç değildir; aynı yükte JFR ile doğrulanır.

### Heap headroom ve konteyner

Container-aware JVM cgroup sınırını görür; ancak bütün sınırı heap'e vermek yanlış muhasebedir. JVM heap dışında metaspace, code cache, GC yapıları, direct buffer, thread stack ve native kütüphane taşır. Ayrıca işletim sistemi ve yan süreçler de aynı limit alanını paylaşabilir.

Doğru soru:

```text
Xmx kaç olsun?
```

değil:

```text
canlı veri ne kadar?
allocation rate ne kadar?
GC çevrimi için ne kadar headroom gerekiyor?
native peak ne kadar?
cgroup OOM sınırına kaç MB kalıyor?
```

sorularıdır.

### JIT, tiered compilation ve deoptimization

Uzun yaşayan HotSpot süreci aynı kodu baştan sona aynı biçimde çalıştırmaz. Yorumlayıcı ve tiered compilation çalışma zamanı profili toplar; sıcak yöntemler daha agresif derlenir, inlining kararı çağrı profilinden etkilenir.

Bu yüzden kısa mikro-test ile üretim servisi aynı değildir. Isınma sırasında compilation queue, class loading, cache doldurma ve branch profili oturmamıştır. JIT varsayımı geçersiz olduğunda deoptimization yapılabilir ve kod yeniden derlenebilir; ani latency sıçramaları yalnız GC değildir.

`PrintCompilation` veya inlining günlükleri özel incelemede yararlıdır; sürekli üretim gözlemi için JFR daha bütünlüklüdür.

### CDS ve AppCDS

Class Data Sharing, önceden işlenmiş sınıf metadata'sını paylaşılabilir archive üzerinden kullanarak başlangıç süresi ve bazı bellek maliyetlerini azaltabilir. AppCDS bu alanı uygulama sınıflarına genişletir. Bu optimizasyon request hot path'ini otomatik hızlandırmaz; startup ve footprint problemi varsa ölçülür.

### Direct buffer, metaspace ve classloader sızıntısı

Heap sabitken RSS büyüyorsa yalnız `-Xmx` bakmak yanlıştır. Netty/NIO direct buffer'lar, JNI kütüphaneleri, thread stack'leri ve class metadata native alandadır. Dinamik classloader üreten uygulamada sınıflar unload edilemiyorsa metaspace büyür; kök neden heap leak değildir.

NMT JVM'nin kendi native kategorilerini ayırır ama üçüncü taraf native allocation'ı tam izlemez. Gerekirse işletim sistemi araçlarıyla RSS mapping ve native allocator profili birleştirilir.

### GraalVM Native Image

Native Image kapalı dünya analiziyle erişilebilir kodu AOT derler. Reflection, resource, JNI ve serialization gibi dinamik erişimler reachability metadata gerektirebilir. Temel kazanç düşük startup ve düşük bellek ayak izi olabilir; steady-state throughput'un JIT'ten otomatik yüksek olduğu varsayılmaz.

Profile-Guided Optimization, temsil edici iş yükünden profil toplayıp native executable'ı yeniden derleyerek sıcak yolları AOT aşamasına taşır. Buradaki kritik kelime "temsil edici"dir. Yanlış profille PGO da yanlış işi optimize eder.

Native Image özellikle kısa ömürlü process, yoğun scale-to-zero/startup SLO veya sıkı footprint hedefinde güçlü adaydır. Uzun ömürlü, saatlerce ısınmış, yüksek verimli servis için HotSpot JIT hâlâ çok güçlüdür. İki taraf aynı trafik ve aynı kaynak bütçesiyle ölçülür.

## 41. Serileştirme ve ağ

Veritabanından az veri çekip API'de dev JSON grafiğine dönüştürmek optimizasyon değildir.

Yanıt maliyeti:

```text
DB'den gelen bayt
-> Java nesnesi
-> JSON baytı
-> ağ
-> istemci parse
```

zincirinin toplamıdır.

Gereksiz alanı en son serileştirmede atmak yerine mümkünse veritabanından hiç çekmemek daha ucuzdur.

HTTP keep-alive, bağlantı havuzu ve HTTP/2 çoklu küçük çağrılarda el sıkışma maliyetini azaltır. Sıkıştırma büyük metin yanıtında kazançlı, küçük veya zaten sıkıştırılmış içerikte gereksiz olabilir.


### HTTP/2 ve HTTP/3

HTTP/2 tek bağlantı üzerinde birden çok stream'i multiplex eder ve HPACK ile header tekrarını azaltır. Bu, her istek için ayrı TCP/TLS kurma maliyetini düşürür; ancak TCP taşıma katmanındaki paket kaybı aynı bağlantının birden çok stream'ini etkileyebilir.

HTTP/3 aynı uygulama semantiğini QUIC üzerinde taşır. Bağımsız stream'ler ve transport düzeyindeki davranış özellikle kayıplı/uzak ağlarda avantaj sağlayabilir. Sunucu tarafında HTTP/3'ü açmak, veritabanı veya JSON hot path'i yavaşsa mucize yaratmaz; önce hangi katmanın bütçeyi tükettiği ölçülür.

### Sıkıştırma

GZIP/Brotli seçimi yalnız sıkıştırma oranı değildir:

```text
payload boyutu
CPU bütçesi
istemci desteği
network RTT/bandwidth
cache davranışı
```

birlikte değerlendirilir. Küçük payload'da header ve CPU maliyeti kazancı aşabilir. JPEG, MP4, ZIP gibi zaten sıkıştırılmış veri yeniden sıkıştırılmaz.

### Jackson ve sıcak yol

Spring Boot 4 dönemi Jackson 3 geçişiyle çakışır. Jackson 3 Maven groupId ve Java package'larını `tools.jackson` altında değiştirmiş, birçok varsayılan ve API yüzeyini yenilemiştir. Jackson 2 için ölçülmüş Afterburner/Blackbird sonucu yeni major sürüme körlemesine taşınmaz.

Serileştirmede önce nesne grafiği küçültülür:

```text
az alan
az nested nesne
az String dönüşümü
az ara koleksiyon
```

Sonra serializer seçilir. Protobuf, Avro veya MessagePack daha küçük/ucuz olabilir; fakat şema yönetimi, insan okunabilirliği, istemci ekosistemi ve uyumluluk maliyeti ekler. JSON yeterliyse ikili biçime geçmek performans mühendisliği değildir.

## 42. Şema sahipliği ve ORM

ORM şemayı bilmeden doğru çalışamaz. Primary key, unique constraint, foreign key, indeks ve sütun türleri yalnız DBA konusu değildir; uygulama erişim planının parçasıdır.

Aynı şekilde uygulama sorgu desenini bilmeden DBA doğru indeks tasarlayamaz.

İyi sınır:

```text
uygulama ekibi:
  erişim deseni, iş kuralı, veri hacmi, SLO

DBA / veri tabanı ekibi:
  fiziksel plan, indeks, istatistik, depolama, bakım

ortak:
  ölçüm ve değişiklik sonucu
```

şeklindedir.

Canlı şemayı uygulamanın otomatik değiştirmesi kontrolsüz üretim ortamında risklidir. Şema değişikliği ayrı, gözden geçirilebilir ve geri dönüşü planlanmış süreç olmalıdır.

## 43. Performans testi

Test verisi üretim büyüklüğüne, dağılımına ve ilişki yoğunluğuna yaklaşmalıdır. Bir milyon aynı tip kayıt, gerçek hayattaki seçicilik dağılımını temsil etmeyebilir.

Ayrı testler:

- soğuk başlangıç,
- ısınmış cache,
- sabit yük,
- kademeli yük,
- ani sıçrama,
- uzun süreli dayanım,
- arıza ve geri kazanım,
- replica lag,
- backlog tüketimi

için yapılır.

Başarım testi yorum değil kabul ölçütü üretmelidir:

```text
P99 < hedef
hata oranı < hedef
DB acquisition < hedef
sorgu sayısı <= sınır
heap kararlı
backlog büyümüyor
```

Test bittikten sonra kaynakların normale dönmesi de doğrulanır; durmayan kuyruk veya büyüyen heap kararlı sistem değildir.


### Açık ve kapalı yük modeli

Yük aracının marka adı, trafik modelinden daha az önemlidir. Kapalı modelde sanal kullanıcı önceki isteğin bitmesini bekleyip sonra yenisini gönderir. Sistem yavaşladıkça gönderim hızı da düşer ve gerçek kuyruk baskısı gizlenebilir.

Açık modelde varış hızı servis süresinden bağımsız tutulur:

```text
hedef: 1000 istek/s
servis yavaşlasa da
1000 istek/s gelmeye devam eder
```

Bu, gerçek dış dünyadaki sabit/harici trafik kaynağına daha yakın davranır ve coordinated omission riskini azaltır.

k6 arrival-rate executors bu modeli doğrudan kurabilir. Gatling de sabit/kademeli arrival profilleriyle aynı yük hipotezini bağımsız araçta sınamak için kullanılabilir. İki araç aynı iş yükünde anlamlı biçimde ayrışıyorsa uygulamayı tune etmeden önce benchmark düzeni incelenir.

### Trafik şekilleri

Tek bir "10 dakika 500 RPS" testi yeterli değildir:

```text
ramp      kapasite eğrisini görür
steady    kararlı durum davranışını ölçer
spike     queue/load shedding sınırını görür
soak      leak, compaction, GC ve bakım etkisini çıkarır
recovery  backlog ve yeniden bağlanma davranışını ölçer
```

Think time ve pacing gerçek kullanıcı davranışını temsil etmelidir. Test verisi aynı cache anahtarını sürekli vuruyorsa gerçek seçiciliği değil cache benchmark'ını ölçer.

### CI eşikleri ve regresyon

Performans testi yalnız grafik üretmez; başarısız olabilen bir kabul testi olmalıdır. Ancak threshold gürültünün altında seçilmez. Donanım, kernel, JVM, veri seti ve yük üreteci mümkün olduğunca sabitlenir; küçük farklar için birden çok run ve güven aralığı gerekir.

Kod değişikliğinden önce/sonra aynı yükte JFR veya flame graph almak, "P99 yüzde 12 düştü" sonucunun nedenini açıklar. Hızlandı ama allocation, DB blok okuma veya ağ baytı değişmediyse ölçüm varyansı ihtimali araştırılır.

## 44. Kapasite planlama

Başlangıç SLO'dur:

```text
P99 hedefi
hata oranı
tepe istek/s
veri büyümesi
arıza senaryosu
```

Sonra düşük yük hizmet süresi, doyma noktası ve güvenli çalışma payı ölçülür.

Örnek sayısı:

```text
tepe yük / tek örneğin güvenli kapasitesi
```

ile bulunur; ardından `n-1` veya gerekiyorsa `n-2` arıza payı eklenir.

CPU, G/Ç ağırlıklı serviste tek başına autoscaling sinyali değildir. Kuyruk uzunluğu, bağlantı bekleme, P99 ve backlog çoğu zaman daha erken sinyal verir.


### USE, RED ve darboğazdan örnek sayısına

Kapasite hesabında önce servis düzeyinde RED, sonra her kritik kaynak için USE okunur. CPU yüzde 40 iken bağlantı havuzu yüzde 100 doluysa CPU'dan instance sayısı çıkarmak yanlış kapasite modelidir.

Tek instance güvenli kapasitesi, ilk hata veya timeout noktasından değil SLO'nun bozulmaya başladığı noktadan alınır. Peak kapasiteyle steady kapasite arasında headroom bırakılır.

### Burst ve arıza bütçesi

`n-1` yalnız bir sunucunun fiziksel kaybı değildir. Bir availability zone, DB node, cache shard veya network path kaybı da kapasiteyi azaltabilir. Failover sonrasında kalan düğümlerin yüzde 100'de çalışması "yüksek erişilebilir" değildir; yalnız arıza anını bir sonraki doygunluğa taşımaktır.

### İş başına maliyet

Kaynak maliyeti yalnız bulut faturası değildir. Kapalı ağda da CPU-saniye, DB-saniye, IOPS, ağ baytı, GPU zamanı ve operasyonel bakım kapasite maliyetidir.

```text
maliyet / iş = toplam kaynak tüketimi / tamamlanan iş
```

aynı donanımda daha çok iş yapabilmeyi ölçer. P99 düşerken iş başına CPU iki katına çıktıysa optimizasyonun bedeli kaydedilir.

Autoscaling sinyali yükün nedeni değil sonucu olmamalıdır. CPU çok geç yükseliyorsa kuyruk, pending connection, consumer lag veya request concurrency daha erken sinyal olabilir. Scale-out süresi de modele dahildir; yeni instance 90 saniyede hazır oluyorsa 5 saniyelik spike'ı autoscaling çözmez, headroom ve load shedding çözer.

## 45. Optimizasyon önceliği

En yavaş tek sorgu her zaman ilk hedef değildir.

Örnek:

```text
A: günde 1 kez × 5 s       = 5 s/gün
B: saniyede 100 × 20 ms    = 172 800 s/gün DB süresi
```

B sorgusundaki küçük kazanç toplam kapasitede çok daha değerlidir.

Öncelik:

```text
toplam tüketilen kaynak
× kullanıcı etkisi
× değişiklik riski
```

ile belirlenir.

## 46. Sık yapılan hatalar

### Ayar kopyalamak

Başka sistemde `maximumPoolSize=50` iyi çalıştı diye aynı değeri taşımak mühendislik değildir. Veri tabanı çekirdeği, sorgu süresi, ağ ve uygulama örnek sayısı farklıdır.

### Her şeyi önbelleklemek

Cache miss maliyeti çözülmeden yüksek hit ratio güven vermez. Restart sonrası sistem çöküyorsa önbellek bağımlılığı kapasite borcudur.

### Her şeye indeks eklemek

İndeks sayısı arttıkça yazma ve bakım maliyeti büyür. Kullanılmayan indeks de canlı bir veri yapısıdır.

### Her şeyi entity yapmak

Rapor ve liste için entity lifecycle çoğu zaman gereksizdir. DTO projection daha açık ve ucuzdur.

### Transaction'ı geniş tutmak

"Tek metot transaction olsun" bağlantı, kilit ve MVCC ömrünü iş mantığının tümüne bağlar. İşlem veri bütünlüğünün gerektirdiği en küçük sınırdır.

### Çok bağlantıyı kapasite sanmak

Havuzu büyütmek DB CPU'sunu, diskini veya kilit paralelliğini artırmaz.

### Yüksek thread sayısını verim sanmak

Bekleyen thread iş yapmaz. Sanal thread beklemenin maliyetini azaltır, kaynağın kapasitesini büyütmez.

### Full scan'i otomatik hata sanmak

Optimizer düşük seçicilikte tam taramayı bilinçli seçebilir. Plan iş miktarıyla okunur.

### Native SQL'i otomatik hızlı sanmak

Native SQL ORM çeviri maliyetini aşabilir ama kötü plan, fazla satır veya fazla round-trip varsa hiçbir şey çözmez. Soyutlamayı ancak ölçülen maliyet gerekçesiyle bypass et.

## 47. Uygulama karar sırası

Bir okuma yavaşsa:

```text
1. gerçekten bu veri gerekli mi?
2. gereksiz satır var mı?
3. gereksiz sütun var mı?
4. sorgu sayısı fazla mı?
5. round-trip fazla mı?
6. uygun indeks var mı?
7. tahminler doğru mu?
8. plan doğru mu?
9. kilit / replica / I/O beklemesi var mı?
10. ancak sonra cache düşün
```

Bir yazma yavaşsa:

```text
1. transaction gereğinden uzun mu?
2. kaç round-trip var?
3. batch gerçekten çalışıyor mu?
4. kimlik stratejisi batch'i bozuyor mu?
5. gereksiz indeks var mı?
6. row-by-row yerine set-based olabilir mi?
7. lock contention var mı?
8. WAL/redo/fsync sınırı mı?
9. storage write amplification mı?
10. veriyi farklı iş yüküne ayırmak gerekir mi?
```

Sistem yük altında çöküyorsa:

```text
1. nerede kuyruk büyüyor?
2. giriş hızı kaç?
3. hizmet hızı kaç?
4. timeout çalışıyor mu?
5. retry yükü büyütüyor mu?
6. bağlantı havuzu bekliyor mu?
7. DB doygun mu?
8. cache stampede var mı?
9. consumer backlog DB'yi eziyor mu?
10. yük azaltma mekanizması var mı?
```

## 48. Genel kavramsal çerçeve

Bu notun tamamı birkaç kurala iner.

**Darboğaz hareket eder.** Thread maliyeti azalınca bağlantı havuzu, havuz iyileşince SQL, SQL iyileşince serileştirme veya ağ görünür olur. Yeni darboğaz başarısızlık değil ilerlemenin sonucudur.

**Her optimizasyon maliyet taşır.** İndeks yazmayı, cache tutarlılığı, batch gecikmeyi, replication güncelliği, partition yönetimi, denormalizasyon yeniden üretim ve senkronizasyon maliyetini artırır.

**Soyutlama maliyeti kaldırmaz.** JPA SQL'i görünmez kılabilir; veritabanı yine SQL, plan, blok ve kilitle çalışır.

**Kaynak gerçek ile türetilmiş veriyi ayır.** Cache, indeks, arama motoru, materialized view ve analitik kopya yeniden üretilebiliyorsa hata alanı küçülür.

**Öngörülebilirlik tepe hızdan değerlidir.** P99'u kararlı 40 ms olan servis, ortalaması 10 ms fakat düzenli 2 saniye sıçrayan servisten çoğu kurumsal işte daha iyidir.

**Kuyruğu görünür yerde tut.** Uygulama girişinde sınırlı bekleme, veritabanının içinde sınırsız kilit ve bağlantı kuyruğundan daha yönetilebilirdir.

**En hızlı sorgu çalışmayan sorgudur.** Gereksiz veri hiç istenmez, gereksiz ilişki yüklenmez, gereksiz `COUNT` çalışmaz, aynı sonuç boş yere tekrar hesaplanmaz.

**Doğruluk performanstan önce gelir.** Primary key, unique constraint, isolation ve idempotency kaldırılarak elde edilen verim gerçek optimizasyon değildir.

**Ölçmeden yapılan ayar varsayımdır.** Profil, plan ve yük testi olmayan performans kararı doğrulanmamış hipotezdir.

## 49. Temel ayrımlar

- Yanıt süresi != verim.
- Ortalama != P99.
- Yüzdelik ortalaması != birleşik yüzdelik.
- Kullanım oranı != kapasite payı.
- CPU kullanımı != doygunluk.
- Thread sayısı != gerçek paralellik.
- Sanal thread != sınırsız DB eşzamanlılığı.
- Bağlantı havuzu != veritabanı kapasitesi.
- Bağlantı bekleme != sorgu yürütme süresi.
- `flush` != `commit`.
- `flush` != `clear`.
- Persistence context != ikinci düzey cache.
- `readOnly` != yazmayı mutlak yasaklayan güvenlik sınırı.
- `IDENTITY` != `SEQUENCE`.
- Primary key != yalnız performans indeksi.
- Unique constraint != uygulama içi `exists` kontrolü.
- N+1 != tek bir yavaş sorgu.
- `EAGER` != N+1 çözümü.
- `JOIN FETCH` != sayfalama.
- DTO != entity.
- Fetch size != toplam sonuç boyutu.
- Offset != keyset sayfalama.
- `COUNT(*)` != her sayfanın zorunlu parçası.
- Batch API çağrısı != gerçek ağ batch'i.
- Row-by-row != set-based işlem.
- Bind parametresi != SQL metni birleştirme.
- Plan cache != sonuç cache'i.
- Tahmini satır != gerçek satır.
- Full scan != otomatik hata.
- İndeks taraması != otomatik hızlı yol.
- İndeks != bedelsiz okuma hızlandırıcı.
- B-tree != her iş yükünün tek indeksi.
- Bölümleme != indeks.
- Bölümleme != sharding.
- Replica != her zaman güncel kopya.
- Eşzamansız çoğaltma != commit sonrası sıfır veri kaybı.
- CDC != tam olarak bir kez dış yan etki.
- Cache != kaynak gerçek.
- Materialized view != sanal view.
- OLTP != OLAP.
- Satır depolama != sütun depolama.
- B-tree != LSM.
- Read amplification != write amplification.
- MVCC != serileştirilebilirlik.
- Snapshot isolation != write-skew koruması.
- Optimistic lock != tüm iş kuralı tutarlılığı.
- Deadlock != kalıcı hata.
- Retry != her hatanın çözümü.
- Idempotency != duplicate oluşmaz varsayımı.
- Event sourcing != her CRUD sistemi için doğru mimari.
- CQRS != yalnız read/write service sınıfı ayırmak.
- Şema değişikliği != tek deployment anı.
- Sıfır doldurma gibi bir performans hilesi yoktur: veri sistemlerinde de ek kaynak fiziksel sınırı değiştirmez.
- Native SQL != otomatik en hızlı yol.
- Native image != daha hızlı veritabanı.
- Büyük heap != daha az GC sorunu.
- Çok sunucu != doğrusal ölçek.
- Daha hızlı != daha iyi; hangi maliyet karşılığında olduğu belirtilmelidir.

## Kaynakça

- Köker, Muhammet Ali. *Kurumsal Veri Tabanı İndeksleme Gereksinimi*. T.C. İçişleri Bakanlığı Arge Notları, 12.12.2025. Gerçek kurumsal uygulama ve proje deneyimlerinden oluşturulan çalışma notu.
- Köker, Muhammet Ali. *Kurumsal Veri Tabanı Erişim Katmanı Optimizasyonu*. T.C. İçişleri Bakanlığı Arge Notları, 22.12.2025. Gerçek kurumsal uygulama ve proje deneyimlerinden oluşturulan çalışma notu.
- Kleppmann, Martin; Riccomini, Chris. *Designing Data-Intensive Applications: The Big Ideas Behind Reliable, Scalable, and Maintainable Systems*. 2nd ed. O'Reilly Media, 2026.
- Mihalcea, Vlad. *High-Performance Java Persistence*. Leanpub sürümü, 9 Haziran 2020. https://vladmihalcea.com/books/high-performance-java-persistence/
- Red Hat / Hibernate. *Hibernate ORM User Guide*. https://hibernate.org/orm/documentation/
- Eclipse Foundation. *Jakarta Persistence Specification*. https://jakarta.ee/specifications/persistence/
- Spring. *Spring Boot Reference Documentation*. https://docs.spring.io/spring-boot/reference/
- Spring. *Spring Framework Reference - Data Access and Transaction Management*. https://docs.spring.io/spring-framework/reference/data-access.html
- Oracle. *Oracle Database SQL Tuning Guide*. https://docs.oracle.com/en/database/oracle/oracle-database/
- Oracle. *Oracle Database Concepts*. https://docs.oracle.com/en/database/oracle/oracle-database/
- Oracle. *Java JDBC Documentation*. https://docs.oracle.com/en/java/javase/
- PostgreSQL Global Development Group. *PostgreSQL Documentation - Indexes, Performance and Concurrency Control*. https://www.postgresql.org/docs/current/
- HikariCP. *About Pool Sizing*. https://github.com/brettwooldridge/HikariCP/wiki/About-Pool-Sizing
- OpenJDK. *JEP 444: Virtual Threads*. https://openjdk.org/jeps/444
- OpenJDK. *JEP 491: Synchronize Virtual Threads without Pinning*. https://openjdk.org/jeps/491
- OpenJDK. *JEP 439: Generational ZGC*. https://openjdk.org/jeps/439
- Berenson, Hal; Bernstein, Philip; Gray, Jim; Melton, Jim; O'Neil, Elizabeth; O'Neil, Patrick. “A Critique of ANSI SQL Isolation Levels.” *SIGMOD Record*, 24(2), 1995. https://doi.org/10.1145/568271.223785
- Gunther, Neil J. *Guerrilla Capacity Planning*. Springer, 2007.
- Gregg, Brendan. *Systems Performance: Enterprise and the Cloud*. 2nd ed. Addison-Wesley, 2020.
- Goetz, Brian et al. *Java Concurrency in Practice*. Addison-Wesley, 2006.
- Silberschatz, Abraham; Korth, Henry F.; Sudarshan, S. *Database System Concepts*. McGraw-Hill.
- Date, C. J. *An Introduction to Database Systems*. Addison-Wesley.
- Garcia-Molina, Hector; Ullman, Jeffrey D.; Widom, Jennifer. *Database Systems: The Complete Book*. Pearson.
- Elmasri, Ramez; Navathe, Shamkant B. *Fundamentals of Database Systems*. Pearson.
- Yarımağan, Ünal. *Veri Tabanı Sistemleri*.
- Apache Software Foundation. *Kafka Documentation*. https://kafka.apache.org/documentation/
- Micrometer. *Micrometer Documentation*. https://docs.micrometer.io/

- İnan, Umur. *Spring Boot 4 Performance*. CineTrack üzerinden ölçüm, JVM, Spring, veri, Kafka, kapasite ve GraalVM başarım çalışması.
- Oracle. *Java SE 25 Garbage Collection Tuning Guide*. https://docs.oracle.com/en/java/javase/25/gctuning/
- Oracle. *Java SE 25 Troubleshooting Guide; Java Flight Recorder and Native Memory Tracking*. https://docs.oracle.com/en/java/javase/25/troubleshoot/
- OpenJDK. *JEP 474: ZGC: Generational Mode by Default*. https://openjdk.org/jeps/474
- OpenJDK. *JEP 525: Structured Concurrency (Sixth Preview)*. https://openjdk.org/jeps/525
- Spring. *Spring Boot 4 Common Application Properties; Virtual Threads*. https://docs.spring.io/spring-boot/4.0/appendix/application-properties/index.html
- Micrometer. *Histograms and Percentiles*. https://docs.micrometer.io/micrometer/reference/concepts/histogram-quantiles.html
- async-profiler. *async-profiler*. https://github.com/async-profiler/async-profiler
- Grafana Labs. *k6: Open and Closed Models*. https://grafana.com/docs/k6/latest/using-k6/scenarios/concepts/open-vs-closed/
- PostgreSQL Global Development Group. *PostgreSQL 18: Resource Consumption, Routine Vacuuming, pg_stat_statements*. https://www.postgresql.org/docs/18/
- PgBouncer. *Configuration*. https://www.pgbouncer.org/config
- Caffeine. *Design, Efficiency and Refresh*. https://github.com/ben-manes/caffeine/wiki
- Redis. *Pipelining*. https://redis.io/docs/latest/develop/using-commands/pipelining/
- Apache Kafka. *Producer and Consumer Configuration*. https://kafka.apache.org/documentation/
- GraalVM. *Native Image Reference Manual*. https://www.graalvm.org/latest/reference-manual/native-image/
- FasterXML. *Jackson 3 Migration and Release Documentation*. https://github.com/FasterXML/jackson
- IETF. *RFC 9113: HTTP/2*. https://www.rfc-editor.org/rfc/rfc9113
- IETF. *RFC 9114: HTTP/3*. https://www.rfc-editor.org/rfc/rfc9114

## Bu Çalışmaya Atıf

Köker, M. A. (2025). Java Tabanlı Veri Sistemlerinde Yüksek Başarım. alikoker.com.tr. https://alikoker.com.tr/java-tabanli-veri-sistemlerinde-yuksek-basarim

- BibTeX: https://alikoker.com.tr/java-tabanli-veri-sistemlerinde-yuksek-basarim.bib
- RIS: https://alikoker.com.tr/java-tabanli-veri-sistemlerinde-yuksek-basarim.ris
- CSL-JSON: https://alikoker.com.tr/java-tabanli-veri-sistemlerinde-yuksek-basarim.csl.json
