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.
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:
ö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:
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 maliyetBir 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:
L = λ · W
L : sistemde aynı anda bulunan iş
λ : varış hızı
W : sistemde geçirilen ortalama süreBu 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:
1000 istek/s
istek başına DB bağlantısı tutma süresi = 5 ms
L = 1000 × 0.005 = 5Kuramsal 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:
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:
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:
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:
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:
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:
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:
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:
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:
RED: rate, errors, duration
USE: utilization, saturation, errorsBir 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:
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:
küçük havuz
-> ölçülebilir bekleme
-> kontrollü zaman aşımıAşırı büyük havuz:
ç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.
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 yazTutarlı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ı:
uygulama örneği × örnek başına havuzile 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:
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çesimaximumPoolSize 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:
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:
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:
10 000 ucuz sanal thread
-> 10 DB bağlantısı
-> 4 uzak servis kotası
-> 1 sıcak kilitzincirinde 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:
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:
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ı yapmaKilitlenme
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ı:
aynı iş kimliği
-> ilk sonuç saklanır
-> tekrar çağrı aynı sonucu görürmodeliyle 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ı:
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:
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
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:
servis transaction'ı
-> gereken veriyi açıkça getir
-> DTO oluştur
-> transaction kapanır
-> web katmanı DB'ye dokunmazopen-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:
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 PKolarak 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
1 kök sorgu
+ N ilişki sorgusu
= N+1Her 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:
DTO projectionvarlık gerekiyorsa:
JOIN FETCH / entity graphilişki yalnız bazı kayıtlarda gerekliyse:
batch fetchkullanı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:
1. sayfalanmış kök ID'lerini getir
2. bu ID'lerin ilişkilerini ayrı sorguda getirolabilir.
Çok koleksiyon
İki büyük koleksiyonu tek join ile çekmek:
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çü:
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:
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:
stream kısa işlem + düzenli tüketim + kesin closeolmalı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
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
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.
10 000 insert
tek tek -> 10 000 gönderim
batch 100 -> yaklaşık 100 grupGerç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:
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ı:
tek set-based SQL
-> bulk/batch
-> zorunluysa kontrollü satır işlemeolmalı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ı:
parse
-> anlam çözümleme
-> plan seçimi
-> yürütme
-> sonuçKısa OLTP sorgusunda hard parse maliyeti sorgunun kendisine yaklaşabilir.
Bind parametreleri
WHERE user_id = ?aynı SQL metninin tekrar kullanılmasını sağlar ve SQL enjeksiyonuna karşı temel savunmadır.
Değerleri metne birleştirmek:
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:
tahmini satır
gerçek satırfarkı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:
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:
önce SELECT var mı?
sonra INSERTyaklaşı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
(a, b, c)indeksi soldan önek kuralına göre çalışır.
Genel sıra:
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:
WHERE active = truegibi sorgularda indeks boyutunu dramatik azaltabilir. Veritabanı desteği ve sözdizimi ürüne bağlıdır.
İfade indeksi
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:
INSERT
UPDATE(indexed column)
DELETEsırasında bakım ister; WAL/redo hacmini, depolama alanını ve cache tüketimini artırır.
Bu yüzden:
indeks yokkadar:
her sütunda indeksde 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:
indeks oku
-> rowid/tuple konumu bul
-> tabloya rastgele gitmaliyeti 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:
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ı:
WAL
-> memtable
-> SSTable
-> compactionzinciriyle 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:
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.
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 verimAynı 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:
OLTP
-> CDC / ETL
-> analitik sistemyaklaşı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.
kaynak gerçek
-> dönüşüm
-> türetilmiş görünümTü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:
yaz
hemen okuakışı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.
transaction DB
-> değişiklik günlüğü
-> CDC
-> kuyruk / arama / analitik / cacheBu 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:
2026-07
2026-08
2026-09gibi 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:
partition key + sort keyile 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:
ucuz write -> pahalı read
pahalı write -> ucuz readDağı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:
kaynak gerçek normalize
okuma görünümü gerekirse türetyaklaşı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:
komut
-> doğrulama
-> olay
-> materialized view'larAvantajları:
- 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:
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ırBu 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:
cache miss
restart
toplu expiryanı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:
hit rate
miss sayısı
miss yükleme maliyeti
load failure
load P99
eviction sayısı / ağırlığı
cache boyutubirlikte 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:
L1 Caffeine
-> L2 Redis
-> DBokuma 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:
büyük batch -> daha yüksek verim, daha fazla gecikme
küçük batch -> daha düşük gecikme, daha fazla protokol maliyetiBö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:
kademeli tüketim
hız sınırı
arka uç doygunluğuna göre backpressureile 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:
daha az ağ ve broker I/O
karşılığında
producer/broker/consumer CPUacks=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:
lag records
lag time
tüketim rate
üretim rate
rebalance
processing P99
arka uç saturationKesinti 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ı:
sınırsız kuyruk
değil
sınırlı kuyruk
-> timeout
-> rate limit
-> load sheddingolmalı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.
toplam bütçe
-> bağlantı
-> kuyruk
-> sorgu
-> alt servisolarak 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.
1 kullanıcı isteği
× 3 gateway retry
× 3 servis retry
× 3 DB retry
= 27 girişimgibi 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ı:
gecikme
bellek
indeks oluşturma maliyeti
recall
güncelleme maliyetiarası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:
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:
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:
heap
+ metaspace
+ thread stacks
+ direct buffers
+ code cache
+ native libraries
+ GC structurestoplamı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:
warm-up
-> kararlı yük
-> ölçümolarak 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:
Xmx kaç olsun?değil:
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:
DB'den gelen bayt
-> Java nesnesi
-> JSON baytı
-> ağ
-> istemci parsezincirinin 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:
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:
az alan
az nested nesne
az String dönüşümü
az ara koleksiyonSonra 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:
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:
P99 < hedef
hata oranı < hedef
DB acquisition < hedef
sorgu sayısı <= sınır
heap kararlı
backlog büyümüyorTest 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:
hedef: 1000 istek/s
servis yavaşlasa da
1000 istek/s gelmeye devam ederBu, 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:
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çerThink 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:
P99 hedefi
hata oranı
tepe istek/s
veri büyümesi
arıza senaryosuSonra düşük yük hizmet süresi, doyma noktası ve güvenli çalışma payı ölçülür.
Örnek sayısı:
tepe yük / tek örneğin güvenli kapasitesiile 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.
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:
A: günde 1 kez × 5 s = 5 s/gün
B: saniyede 100 × 20 ms = 172 800 s/gün DB süresiB sorgusundaki küçük kazanç toplam kapasitede çok daha değerlidir.
Öncelik:
toplam tüketilen kaynak
× kullanıcı etkisi
× değişiklik riskiile 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:
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üşünBir yazma yavaşsa:
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:
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
existskontrolü. - 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