İçeriğe geç

Ölçekleme İkilemi: Dikey ve Yatay Ölçekleme

Dikey ölçekleme (scale-up), tek bir makineye kapasite ekler: daha fazla core, bellek, bellek bant genişliği, daha hızlı depolama ve ağ arayüzleri. Yatay ölçekleme (scale-out) ise makine ekler ve işi bu makineler arasında paylaştırır. İkisi de sistemin gecikme hedefi içinde taşıyabildiği istek hızını yükseltmeyi amaçlar ve ikisi de donanım bütçesinin değil, iş yükünün belirlediği bir noktada işe yaramaz hâle gelir. Daha büyük bir makine, istekler onun içindeki bir şey üzerinde seri hâle geldiğinde ya da core’ları zamanlarını paylaşılan veriyi tutarlı tutmakla geçirdiğinde getirisini yitirir. Daha fazla makine ise isteklerin tamamlanmak için birden fazla makineye dokunması gerektiğinde ya da tüm makineler hâlâ paylaşılan tek bir bileşene bağımlı olduğunda getirisini yitirir.

Asıl denge, koordinasyonun nerede gerçekleştiği ve bedelini kimin ödediğidir. Tek makinede core’lar arası koordinasyonu cache-coherence donanımı onlarca ila yüzlerce nanosaniyede yapar; profilde baskın hâle gelene kadar görünmezdir. Makineler arasında ise bunu, veri merkezi içinde round trip’i yüzlerce mikrosaniye tutan bir ağ üzerinden çalışan yazılım protokolleri yapar ve bedeli gecikmede, hata yönetiminde, tutarlılık modelinde ve operasyonel maliyette ortaya çıkar. Dikey ölçekleme sadelik ve güçlü tutarlılık satın alır; karşılığında katı bir tavan ve tek bir hata alanı (failure domain) kabul eder. Yatay ölçekleme pay ve daha küçük hata birimleri satın alır; karşılığında kalıcı bir koordinasyon vergisi öder.

İkinci bir eksen kapasiteden bağımsızdır: servisin kaç hata alanının kaybına dayanması gerektiği. “Kapasite için makine eklemek” ile “erişilebilirlik için makine eklemek” kararlarını tek bir karar gibi ele almak, aşağıda anlatılan ölçekleme hatalarının çoğunun kökenidir. Donanım fiyat eğrileri Maliyet-Performans Eğrileri ve Donanım Sınırları sayfasında, durumun (state) nerede yaşayabileceği sorusu ise Stateful ve Stateless Servisler sayfasında ele alınır.

Scale-up: one machine

coherence fabric, ns

Requests

Many cores

Shared memory and locks

Stateless scale-out

network, us to ms

network, us to ms

network, us to ms

Requests

Load balancer

App 1

App 2

App N

One shared database

Partitioned scale-out

Requests

Router

Owner of keys A-H

Owner of keys I-Z

Scale-up: one machine

coherence fabric, ns

Requests

Many cores

Shared memory and locks

Stateless scale-out

network, us to ms

network, us to ms

network, us to ms

Requests

Load balancer

App 1

App 2

App N

One shared database

Partitioned scale-out

Requests

Router

Owner of keys A-H

Owner of keys I-Z

Üç topoloji ve her birinde paylaşılan, değişebilen durumun (shared mutable state) bulunduğu yer. Gecikme etiketleri büyüklük mertebesini gösterir.

İlk topolojide paylaşılan durum bellektedir; koordinasyon ucuzdur ama tek bir kutuyla sınırlıdır. İkincisinde uygulama katmanı yatay ölçeklenmiştir, ancak her istek hâlâ tek bir veritabanında birleşir; yani seri hâle gelme noktası ortadan kalkmamış, yer değiştirmiştir. “Yatay ölçeklenip” hızlanmayan sistemlerin en yaygın şekli budur. Paylaşılan kaynağı her anahtara tam olarak tek bir sahip atayarak gerçekten ortadan kaldıran yalnızca üçüncüsüdür; aynı zamanda birden fazla sahibe yayılan istekleri ortaya çıkaran da yalnızca odur ve yatay ölçeklemenin maliyeti tam olarak burada yoğunlaşır.

Dikey Ölçeklemenin Tavanı: Seri İş ve Coherence Trafiği

Section titled “Dikey Ölçeklemenin Tavanı: Seri İş ve Coherence Trafiği”

Daha büyük bir makine, throughput’u yalnızca ek core’larında aynı anda çalışabilen iş için artırır. Bunu iki ayrı mekanizma sınırlar ve her biri farklı bir çözüme yanıt verir. Birincisi, özel (exclusive) bir kaynak için kuyrukta beklemektir: bir kilit, bir log flush’ı, tek thread’li bir bileşen. İkincisi, core’ların paylaşılan belleğin içeriği üzerinde uzlaşmak için yaptığı iştir. Birincisi throughput eğrisini düzleştirir; ikincisi aşağı doğru büker.

Her iş biriminin S oranındaki kısmı seri çalışmak zorundaysa ve geri kalanı N core’a kusursuz biçimde paralelleşiyorsa, Amdahl Yasası hızlanmayı 1 / (S + (1 - S) / N) olarak verir; üst sınır 1 / S’dir. %4 seri işte tavan 25x’tir; sekiz core 6.2x, 32 core 14.3x, 128 core ise 21.1x sağlar. Makineyi 32 core’dan 128 core’a, yani dört katına çıkarmak %48 fazla throughput getirir ve her yeni core bir öncekinden daha az değerlidir.

İstek işleyen bir sistemde seri pay nadiren tek thread’li bir fonksiyondur. Her isteğin, diğer tüm isteklerin de ihtiyaç duyduğu bir şeyi tutarak geçirdiği süre payıdır: bir sayaç satırı üzerindeki kilit, bir index sayfası latch’i, bir write-ahead log (WAL) flush’ı, yeterli slotu olmayan bir connection pool. Normalde birbirinden bağımsız istekler bunun arkasında sıraya girer ve oradaki gecikme, kullanım oranı bire yaklaştıkça keskin biçimde artar; bu davranış Kuyruk Teorisi Temelleri sayfasında incelenir.

Gustafson Yasası buradaki düzelticidir: Amdahl problem boyutunu sabit tutar, oysa daha büyük bir makineye genellikle daha fazla bağımsız iş verilir ve seri kısım bununla birlikte büyümez. Ek core’lar daha fazla bağımsız isteğe harcandığında iyi, tek bir kaynakta seri hâle gelen isteklere harcandığında kötü değerlendirilmiş olur.

Coherence: Bir Cache Line’ı Paylaşmanın Bedeli

Section titled “Coherence: Bir Cache Line’ı Paylaşmanın Bedeli”

Her core, yakın zamanda kullandığı belleği kendi özel cache’lerinde tutar; yaygın x86 ve çoğu ARM sunucu işlemcisinde bu birimler cache line adı verilen 64 baytlık bloklardır. İki core aynı line’ın kopyalarını tuttuğunda, cache denetleyicilerinin işlettiği ve line başına tutulan bir durum makinesi olan coherence protokolü, birinin yaptığı yazmanın diğeri tarafından görülmesini garanti eder. Ders kitabı sürümü MESI’dir; Intel MESIF, AMD ise MOESI adlı uzantıları kullanır, ancak ölçekleme açısından önemli olan geçişler üçünde de aynıdır.

local read, no other copy

local read, other copies exist

local write, no bus traffic

local write, invalidate all other copies

remote read, write back and share

remote write, line transferred away

remote write

remote read

local write, no bus traffic

Invalid

Exclusive

Shared

Modified

local read, no other copy

local read, other copies exist

local write, no bus traffic

local write, invalidate all other copies

remote read, write back and share

remote write, line transferred away

remote write

remote read

local write, no bus traffic

Invalid

Exclusive

Shared

Modified

Tek bir core’un gözünden bir cache line’ın MESI durumları. Remote olarak etiketlenen geçişler, başka bir core’un aynı line’a dokunmasından kaynaklanır.

Pahalı geçişler, bir line’ı başka bir core’un cache’inden çıkaran geçişlerdir: başka bir yerde Modified durumunda tutulan bir line’a yazmak isteyen core sahipliği talep etmeli ve diğer core’un onu bırakmasını beklemelidir. Birçok core aynı line’a sırayla yazıyorsa (atomik bir istek sayacı, paylaşılan bir referans sayacı, bir spinlock kelimesi, aynı line’a düşen iki ilgisiz sıcak alan), line cache’ler arasında gidip gelir ve her yazma birkaç cycle yerine core’lar ya da soketler arası bir transfere mal olur. İlgisiz alanların aynı line’ı paylaşması durumuna false sharing denir: programda mantıksal bir paylaşım yoktur, ama donanımda vardır. Core eklemek bu alışverişe daha fazla katılımcı ekler ve olası ikili alışverişlerin sayısı core sayısının karesiyle büyür.

Neil Gunther’in Evrensel Ölçeklenebilirlik Yasası (Universal Scalability Law, USL) iki mekanizmayı tek bir ifadede toplar: C(N) = N / (1 + alpha * (N - 1) + beta * N * (N - 1)). Contention (çekişme) katsayısı alpha seri paydır; coherency katsayısı beta ise ikili alışverişlerin maliyetidir. beta = 0 olduğunda eğri Amdahl’ınkidir. beta sıfırdan büyük olduğu anda throughput N_peak = sqrt((1 - alpha) / beta) noktasında zirve yapar ve ardından düşer: tepe noktasından sonra daha fazla core, thread ya da eşzamanlı bağlantı throughput’u azaltır. beta terimi karesel büyüdüğü için küçük bir instance üzerindeki yük testi, büyük bir instance hakkında neredeyse hiçbir şey söylemez.

Production sistemleri beta’ya verilen iki yanıtı da gösterir. Redis komutları tek bir thread üzerinde yürütür; böylece veri üzerindeki coherence trafiğini ortadan kaldırır ve bir instance’ın tek bir core ile sınırlı olmasını kabul eder. Redis 6’nın I/O thread’leri yalnızca paylaşılan veriye dokunmayan socket okuma ve yazmalarını paralelleştirir. PostgreSQL her bağlantı için bir process çalıştırır; aktif backend’ler çoğaldıkça paylaşılan kilit ve buffer-mapping yapıları üzerindeki contention throughput’u önce düzleştirir, sonra düşürür. PgBouncer gibi pooler’lar, kaç istemci bağlanırsa bağlansın aktif sayıyı tepe noktasının yakınında tutar.

Bazı dikey ölçekleme tavanları işletim sisteminde ve ağ yolunda bulunur. Tek bir thread tarafından accept edilen tek bir dinleme soketi bağlantı kurulumunu seri hâle getirir; Linux, kernel 3.9 ile SO_REUSEPORT seçeneğini ekledi, böylece birden fazla soket aynı port’a bağlanabilir ve kernel bağlantıları bunlar arasında dağıtır. Tüm paketleri tek bir alma kuyruğuna (receive queue) teslim eden bir ağ arayüzü, interrupt işlemeyi tek bir core’da yoğunlaştırır; Receive Side Scaling (RSS) akışları hash’leyerek birden fazla kuyruğa ve core’a dağıtır. Bellek bant genişliği core sayısıyla aynı oranda büyümediği için, belleğe bağlı iş yükleri CPU’lar meşgul olmadan ölçeklenmeyi bırakabilir. Çok soketli makinelerde diğer sokete bağlı belleğe erişmek daha yavaştır (Non-Uniform Memory Access, NUMA); bu, coherence probleminin üzerine eklenen bir yerleşim problemidir.

Yatay Ölçeklemenin Tavanı: Partition’lar Arası İş ve Ortak Kader

Section titled “Yatay Ölçeklemenin Tavanı: Partition’lar Arası İş ve Ortak Kader”

Yatay ölçekleme throughput’u yalnızca paylaşılan bir kaynağı taşımak yerine ortadan kaldırdığında artırır. Stateless uygulama instance’ları birbiriyle hiçbir şey paylaşmaz; bu yüzden onları eklemek uygulama katmanındaki contention’ı ortadan kaldırır, ancak her instance hâlâ aynı veritabanına okur ve yazar. Veritabanının doyduğu noktadan sonra daha fazla uygulama instance’ı yalnızca ona bağlantı ekler ve onu kendi coherency tepe noktasına doğru iter. Hepsi paylaşılan tek bir veri deposuna bağımlı olan çok sayıda servise ayrıştırılmış bir sistem, dağıtık bir sistemin operasyonel maliyetini ve tek bir makinenin ölçekleme sınırını birlikte taşır; bu desen dağıtık monolit olarak incelenir.

Stateful veri için paylaşılan kaynağı ortadan kaldırmak veri bölümleme (partitioning) gerektirir: her anahtar tam olarak bir node’a aittir, böylece tek anahtarlı işlemler diğer node’larla koordinasyon gerektirmez. Partitioning toplam kapasiteyi artırır, ancak her anahtarın kapasitesi hâlâ tek bir makineyle sınırlıdır ve trafiğin orantısız bir payını alan bir anahtar cluster’ın geri kalanından hiçbir şey kazanmaz. Yönetilen sistemler bu sınırı açıkça belirtir: DynamoDB, tablonun toplam provisioned throughput’undan bağımsız olarak partition başına saniyede en fazla 3.000 read capacity unit ve 1.000 write capacity unit sınırını belgeler. Yatay ölçekleme toplamı ölçekler; her bir parçayı ise hâlâ dikey sınırlar yönetir. Çarpık anahtarlar Çarpıklık ve Hot-Spot Problemleri sayfasında ele alınır.

Birden fazla partition’a dokunan bir işlem; bir koordinatör, ek round trip’ler, protokol süresince tutulan kilitler ve bir kurtarma yolu gerektirir. Atomik çok partition’lı yazmalar genellikle iki aşamalı commit ya da onun bir varyantını kullanır. Bu tür her işlem, her katılımcıda ve yerel bir işlemden daha uzun süre kapasite tüketir.

Partition’lar arası işlemlerin payı sabit değildir; anahtarların nasıl yerleştirildiğine bağlıdır. Bir işlem iki anahtara dokunuyorsa ve anahtarlar tekdüze bir hash ile yerleştiriliyorsa, ikisinin aynı partition’a düşme olasılığı 1 / N’dir; dolayısıyla partition’lar arası pay 1 - 1 / N olur ve cluster büyüdükçe %100’e yaklaşır. İki anahtar bilinçli olarak birlikte yerleştirilirse (örneğin bir siparişin her iki satırı da müşteriye göre partition’lanırsa), pay yalnızca gerçekten birden fazla müşteriye yayılan işlemlerin oranında kalır. Basit bir model farkı görünür kılar. Tek partition’lı bir işlemin tek bir node’da bir birim kapasite tükettiğini, iki partition’lı bir işlemin ise prepare ve commit aşamaları ile daha uzun kilit tutma süresi nedeniyle iki node’un her birinde iki birim tükettiğini varsayalım (örnek amaçlı bir çarpan). Bu durumda partition’lar arası pay f için işlem başına ortalama maliyet 1 + 3f olur.

Aggregate throughput vs. partition count (illustrative model)1248163264Partitions N7065605550454035302520151050Throughput, single-node units
Aggregate throughput vs. partition count (illustrative model)1248163264Partitions N7065605550454035302520151050Throughput, single-node units

Üst: doğrusal referans. Orta: ilişkili anahtarlar aynı yerde tutuluyor; işlemlerin %5’i iki müşteriye dokunuyor ve bunların 1 - 1/N’i farklı partition’lara düşüyor. Alt: işlem başına iki anahtar tekdüze hash ile yerleştiriliyor, dolayısıyla işlemlerin 1 - 1/N’i partition’lar arası. Partition’lar arası işlemin maliyet çarpanı örnek amaçlıdır.

Alt eğri iki partition’da tek node’un altına iner: işlemlerin yarısı bir anda partition’lar arası bedeli öder ve cluster, tek makinenin 1.0 birim verdiği yerde 0.8 birim verir. 64 partition’da doğrusal ölçeklemenin yalnızca dörtte birine ulaşır, çünkü neredeyse her işlem bir yerine dört birim öder; aynı donanım üzerinde ilişkili anahtarları bir arada tutan eğri ise doğrusal ölçeklemenin %87’sini korur. Bir sistemin hangi eğride olduğunu node sayısı değil, partition anahtarı belirler; anahtarın nasıl seçileceği colocated join’ler ve veri yerleşimi sayfasında anlatılır. Bir arada yerleştirme fan-out’u da sınırlar: her biri bin çağrıda bir yavaşlayan 50 partition’ı bekleyen bir istek, 1 - 0.999^50 yani yaklaşık %4.9 olasılıkla en az bir yavaş çağrıya denk gelir; Dean ve Barroso’nun The Tail at Scale makalesinde tanımladığı etki budur.

Replikalar Yazma Kapasitesi Değil, Hata Toleransı Ekler

Section titled “Replikalar Yazma Kapasitesi Değil, Hata Toleransı Ekler”

Replikasyon farklı bir kurala tabidir. Raft gibi leader tabanlı ya da consensus grubunda her üye her yazmayı saklar. Bir Raft grubunu üç üyeden beşe büyütmek, tolere edilen hata sayısını birden ikiye ve commit quorum’unu ikiden üçe çıkarır; yazma kapasitesi tek bir leader’ınki olarak kalır ve commit gecikmesi yalnızca artabilir. Read replica’lar okuma kapasitesi ekler, bedeli replikasyon gecikmesi olur. Hem dayanıklılık hem yazma kapasitesi gereken sistemler, her biri bir partition’a ait çok sayıda küçük grup çalıştırır: in-sync replica set’leriyle Kafka partition’ları, CockroachDB range’leri, TiKV region’ları. Kapasite grup sayısından, hata toleransı her grubun boyutundan gelir.

Sayısal Örnek: Tek Sipariş Veritabanı, İki Gelecek

Section titled “Sayısal Örnek: Tek Sipariş Veritabanı, İki Gelecek”

Primary veritabanı 32 vCPU’lu bir makinede çalışan ve gecikme hedefi içinde saniyede 6.000 yazma taşıyan bir sipariş servisi varsayalım. Tepe trafik ayda %4 büyüyor. Bu bölümdeki tüm sayılar örnek amaçlıdır.

1 ile 128 arasındaki eşzamanlılık seviyelerinde yapılan bir yük testi, USL katsayılarını alpha = 0.02 ve beta = 0.0002 olarak veriyor. Göreli kapasite C(32) = 32 / (1 + 0.02 * 31 + 0.0002 * 32 * 31) = 17.60 olduğundan, bir kapasite birimi saniyede yaklaşık 341 yazmaya karşılık gelir. Eğri N_peak = sqrt(0.98 / 0.0002) = 70 noktasında C(70) = 20.92 ile zirve yapar.

Dikey seçenek 64 vCPU’lu bir makinedir: C(64) = 20.87, yani saniyede yaklaşık 7.100 yazma; bugünden %18.6 fazla, saatlik fiyat ise yaklaşık iki katı. Aylık %4 büyümede bu kazanç ln(1.186) / ln(1.04), yani yaklaşık 4.3 ay yeter. 96 vCPU’lu bir makine daha kötüdür: tepe noktasının ötesinde kaldığı için C(96) = 20.32, yani saniyede yaklaşık 6.900 yazma.

Yatay seçenek, her biri 16 vCPU’lu bir makinede çalışan, customer_id’ye göre sekiz partition’dır. Her partition daha düşük eşzamanlılıkta, aynı eğrinin verimli kısmında çalışır: C(16) = 11.87, yani saniyede yaklaşık 4.050 yazma. İki düzeltme gerekir. Yazmaların %3’ü iki müşteriye dokunur (hesaplar arası transferler) ve ortalama 1 + 3 * 0.03 = 1.09 birime mal olur. Ayrıca en büyük müşteri tüm yazmaların %6’sını üretir; dolayısıyla onun partition’ı toplam trafiğin %12.5’ini değil 0.94 / 8 + 0.06 = 17.75% kadarını taşır. İlk doyan en sıcak partition olduğundan sistemin sınırı 4,050 / (0.1775 * 1.09), yani saniyede yaklaşık 20.900 yazmadır. Bu, bugünkü yükün 3.5 katı ya da yaklaşık 32 aylık büyümedir.

Hata aritmetiği daha az belirgindir. Tek primary ile her failover tüm yazmaları erişilemez kılar. Sekiz primary ile bir hata müşterilerin %12.5’ini (sıcak partition için yazmaların %17.75’ini) etkiler, ancak hata verebilecek sekiz makine vardır ve failover’lar yaklaşık sekiz kat daha sık yaşanır. Yılda kaybedilen beklenen yazma-dakikası kabaca aynı kalır; partitioning her olayın etki alanını ve olay sayısını değiştirir. Bu da ancak erişilebilirlik hedefi filonun tamamı için değil, müşteri başına tanımlanmışsa önem taşır.

Dolayısıyla karşılaştırma 7.100’e karşı 20.900 değildir. Düşük mühendislik maliyetiyle 4 aylık pay ile; bir routing katmanı, bir resharding prosedürü, partition’lar arası bir yazma yolu, %6’lık müşteri için bir plan ve sekiz kat daha fazla failover pahasına 32 aylık pay arasındaki karşılaştırmadır. Ekip partition’lı tasarımı 4 aylık süre içinde teslim edemeyecekse, bugün doğru satın alma 64 vCPU’lu makinedir ve aynı gün başlanması gereken doğru proje partition’lı tasarımdır.

Genellikle “yatay” başlığı altında toplanan stratejiler birbirinden çok farklı davranır; tablo bu yüzden onları ayırır.

StratejiBüyüyenBüyümeyenHata birimiNe zaman tercih edilmeli
Dikey, tek nodeTek kutudaki core, bellek, IOPSSeri pay ve coherency maliyeti; N_peak ötesindeki kapasiteServisin tamamıÖlçülen beta küçük, alınabilecek en büyük makine planlama ufkunu karşılıyor ve güçlü tek node tutarlılığı önemli
Stateless yatay ölçeklemeBağımsız istekler için hesaplama gücüPaylaşılan downstream bağımlılıkların kapasitesiTek bir instanceİstekler değişebilir durum paylaşmıyor ve paylaşılan bağımlılığın ölçülmüş payı var
Partition’lı yatay ölçeklemeToplam depolama ve yazma throughput’uAnahtar ve partition başına kapasiteTek bir partitionVeri ya da yazmalar tek makineyi aşıyor ve işlemlerin çoğu tek bir partition anahtarına dokunuyor
Replike grupOkuma kapasitesi (follower’lar), hata toleransıLeader’ın yazma kapasitesiÜyelerin azınlığının kaybı tolere edilirTek bir partition ya da koordinasyon verisi için dayanıklılık ve failover
Diyagonal: az sayıda büyük, partition’lı nodeİkisi birden, daha az koordinasyon katılımcısıylaHer node’un yeniden inşa süresiTek bir büyük nodeNode sayısının koordinasyon maliyetini belirlediği, throughput ağırlıklı stateful sistemler

Diyagonal ölçeklemenin de kendi sınırı vardır: bir node’u kaybetmenin maliyeti boyutuyla birlikte büyür. 8 TB veri tutan ve eşlerinden efektif 250 MB/s hızla (canlı trafiği korumak için uygulanan throttling sonrası, örnek bir değer) yeniden inşa edilen bir node yaklaşık dokuz saat boyunca tasarlanandan bir eksik replikayla yaşar. Replike depolamada node boyutunu genellikle CPU değil, yeniden inşa süresi sınırlar.

Hangi Sınıra Çarpacağınızı Ölçmek

Section titled “Hangi Sınıra Çarpacağınızı Ölçmek”

USL katsayıları ölçümden gelir. Artan eşzamanlılıkta kapalı döngü (closed-loop) bir yük testi çalıştırın, throughput’u sabit bir gecikme hedefinde kaydedin ve C(N)’yi Gunther’in Guerrilla Capacity Planning kitabında anlatıldığı gibi doğrusal olmayan regresyonla uydurun. Eğrinin dirseğinin ötesinde noktalar da ekleyin: tüm noktalar N_peak’in altındaysa yüksek bir alpha ile küçük bir beta veriye eşit derecede uyar ve daha büyük bir makine için yapılan tahmin bir tahminden ibaret kalır.

Contention, CPU kısmen boştayken thread’lerin bloklanmasıyla birlikte yerinde sayan throughput olarak görünür; coherency, CPU doymuşken atomik işlemlerde ve spin döngülerinde harcanan cycle’larla birlikte düşen throughput olarak; partition’lar arası maliyet ise partition sayısıyla büyüyen koordinatör gecikmesi olarak. Linux’ta:

Terminal window
# Per-core utilization: look for single cores pinned at 100% (interrupts, one hot thread)
mpstat -P ALL 1 5
# Voluntary vs. involuntary context switches per thread: high voluntary = blocking
pidstat -w -t -p "$PID" 1 5
# Cache-to-cache transfers of contended lines (true and false sharing)
perf c2c record -p "$PID" -- sleep 10
perf c2c report --stdio | head -60
# Distribution of incoming connections and receive queues across cores
grep -E 'eth0|ens' /proc/interrupts

perf c2c, coherency maliyetinin en doğrudan kanıtıdır: core’lar arasında modified durumda sık transfer edilen cache line’ları, sorumlu kod konumları ve veri offset’leriyle birlikte raporlar. Bu, çekişmeli bir sayacı, ayrı line’lara padding ile ayrılması gereken iki alandan ayırt etmeye yeter.

Hata Modları ve Dışarıdan Görünüşleri

Section titled “Hata Modları ve Dışarıdan Görünüşleri”

Dikey hatalar çoğu zaman kapasite yetersizliği sanılır; yatay hatalar ise çoğu zaman sağlıklı görünen ortalamaların arkasına saklanır.

Yükseltme Sonrası Geriye Dönük Throughput

Section titled “Yükseltme Sonrası Geriye Dönük Throughput”

Tetikleyici: coherency’ye bağlı bir iş yükü daha büyük bir makineye taşınır ya da pool sınırı yükseltilir ve aktif eşzamanlılık N_peak’i aşar. Belirtiler: aynı yükte yerinde sayan ya da düşen throughput, daha yüksek CPU, daha erken başlayan gecikme artışı. Tespit: öncesi ve sonrası için aktif oturum başına throughput, perf c2c transfer sayıları, veritabanının wait-event görünümlerindeki kilit bekleme süresi. Hafifletme: aktif eşzamanlılığı bir connection pooler ya da admission queue ile ölçülen tepe noktasında sınırlamak, gerekirse geri almak. Eşzamanlılık sınırlandığında toparlanma hızlıdır, ancak kök çözüm beta’yı düşüren tasarım çalışmasıdır: striped lock’lar, okuma sırasında toplanan core başına sayaçlar, sıcak alanların padding ile ayrı cache line’lara taşınması, sıcak bir tablonun partition’lanması.

Day 0Migration completesduring low trafficPool limit raised from300 to 900 to usethe new coresDay 1 morningPeak traffic arrivesCPU 90 percent andthroughput 5 percentbelow the oldmachineDay 1 middayRetries raise activesessions furtherp99 latency triplesand upstreamtimeouts beginDay 1 afternoonPool limit cut back to300Throughput recoversabove the oldbaselineWeek 2Contended counterrow replaced bysharded countersLoad test repeatedand new peakrecordedIllustrative incident after moving a primary from 32 to 96 vCPU
Day 0Migration completesduring low trafficPool limit raised from300 to 900 to usethe new coresDay 1 morningPeak traffic arrivesCPU 90 percent andthroughput 5 percentbelow the oldmachineDay 1 middayRetries raise activesessions furtherp99 latency triplesand upstreamtimeouts beginDay 1 afternoonPool limit cut back to300Throughput recoversabove the oldbaselineWeek 2Contended counterrow replaced bysharded countersLoad test repeatedand new peakrecordedIllustrative incident after moving a primary from 32 to 96 vCPU

Bir coherency gerilemesinin şeklini gösteren varsayımsal bir zaman çizelgesi; gerçek bir olay değildir.

Geçiş düşük trafikte başarılı görünür, çünkü beta düşük eşzamanlılıkta küçüktür. Tepe saatte daha fazla core daha fazla aktif oturuma izin verir ve sistemi tepe noktasının ötesine iten tam olarak budur; ardından retry’lar eşzamanlılığı daha da artırır. Bu, yük sıçramasından sonra da sürebilen bir geri besleme döngüsüdür ve Retry Fırtınaları ve Metastabil Hatalar sayfasında metastabil hata olarak anlatılır. Saatler içinde işe yarayan çözüm daha fazla donanım değil, daha az eşzamanlılıktır.

Tetikleyici: dikey ölçeklenmiş bir node üzerindeki tek bir iş yükü paylaşılan bir kaynağı tüketir; örneğin buffer cache’i boşaltan analitik bir sorgu ya da disk bant genişliğini doyuran bir compaction. Belirtiler: tüm tenant’lar ve endpoint’ler aynı anda bozulur. Tespit: tek bir oturum ya da işle ilişkili doygunluk (disk kuyruk derinliği, bellek baskısı, cache isabet oranı). Hafifletme: node içinde izolasyon (ayrı pool’lar, cgroup sınırları, sorgu timeout’ları) ya da iş yükünü bir replikaya taşımak; yani bulkhead ilkesinin tek bir makine içinde uygulanması. Büyük bir makine yalnızca çökmeleri değil, performans hatalarını da tek bir hata alanında toplar.

Yatay Ölçekleme Sırasında Bağlantı Fırtınaları

Section titled “Yatay Ölçekleme Sırasında Bağlantı Fırtınaları”

Tetikleyici: bir autoscaler instance ekler ve her yeni instance paylaşılan veritabanına tam connection pool’unu açar. Belirtiler: veritabanı tam katman büyürken bağlantıları reddeder ya da yavaşlar, yeni instance’lar readiness kontrollerinden geçemez ve autoscaler daha fazla instance ekler.

Shared databaseApp instancesAutoscalerTrafficShared databaseApp instancesAutoscalerTraffic2400 connections vs. limit of 1000, new connections rejectedScale-out raised load on the one component that did not scaleLoad rises, 40 instances at high CPU1Scale from 40 to 120 instances280 new instances open 20 connections each3Connection errors, slow authentication and backend startup4Readiness probes fail, requests retried5CPU still high, scale further6Existing instances time out and reconnect7
Shared databaseApp instancesAutoscalerTrafficShared databaseApp instancesAutoscalerTraffic2400 connections vs. limit of 1000, new connections rejectedScale-out raised load on the one component that did not scaleLoad rises, 40 instances at high CPU1Scale from 40 to 120 instances280 new instances open 20 connections each3Connection errors, slow authentication and backend startup4Readiness probes fail, requests retried5CPU still high, scale further6Existing instances time out and reconnect7

Paylaşılan bir veritabanına karşı autoscaling. Sayılar örnek amaçlıdır.

Tuzak 2. ve 3. adımlardadır: autoscaler, yardıma ihtiyacı olmayan katmandaki CPU’yu ölçer ve veritabanını yükleyen bir şekilde davranır; 7. adıma gelindiğinde kontrol döngüsü hatayı büyütmektedir. Tespit: veritabanı bağlantı sayısının sınıra oranı ve bağlantı kurma hızı, replica sayısıyla aynı grafikte. Hafifletme: pool boyutlarını veritabanı tarafından hesaplamak, araya transaction modunda bir pooler koymak, autoscaler’ın azami replica sayısını ve değişim hızını sınırlamak, gerçek darboğazı yansıtan bir sinyale göre ölçeklemek. Autoscaler davranışı ve stabilizasyon ayarları HPA ve KEDA sayfasında ele alınır.

HataYolDışarıdan belirtiTespit sinyaliHafifletme
Geriye dönük throughputDikeyDaha büyük makine tepe saatte daha yavaşAktif oturum başına throughput düşüyor; perf c2c transferleri artıyorN_peak’te eşzamanlılık sınırı; beta’yı düşürmek
Gizli tek coreDikeyDüşük ortalama CPU’da gecikme artıyormpstat’ta %100’de bir core; dengesiz /proc/interruptsRSS, SO_REUSEPORT, tek thread’li aşamayı kaldırmak
Ortak kaderDikeyHer şey birlikte bozuluyorTek bir işle ilişkili doygunlukNode içi izolasyon, replikaya aktarma
Hot partitionYatayBir partition doymuş, cluster ortalaması sağlıklıPartition başına throughput ve gecikmeAnahtarı bölmek ya da salt’lamak, sıcak anahtarı cache’lemek
Partition’lar arası çöküşYatayPartition eklemek throughput’u artırmıyorN ile büyüyen partition’lar arası pay ve koordinatör gecikmesiPartition anahtarını değiştirmek, ilişkili veriyi bir arada tutmak
Bağlantı fırtınasıYatayUygulama katmanı büyürken veritabanı çöküyorVeritabanı bağlantıları ve sınır, replica sayısıPooler, pool boyutunu veritabanı tarafından belirlemek, autoscaler sınırları
Split brainİkisi de, failover sırasındaİki primary’de ayrışan yazmalarLeader olduğunu iddia eden iki nodeQuorum tabanlı seçim ve fencing

Tablodaki girdilerden yalnızca split brain hizmeti yavaşlatmak yerine veriyi bozar; Split-Brain Problemi ve Fencing Tokens sayfasında ve özellikle veritabanı failover’ı için Failover ve Lider Seçimi sayfasında ele alınır. Yönetilen bir veri deposundaki hot partition modu Hot Partition Problemi sayfasında incelenir.

Karar üç ölçüme ve bir gereksinime dayanır: isteklerin nerede seri hâle geldiği, beta’nın ne kadar büyük olduğu, mevcut en iyi anahtarla işlemlerin ne kadarının partition’lar arası olacağı ve servisin kaç hata alanının kaybına dayanması gerektiği.

Darboğaz ölçülen beta’sı küçük olan stateful bir bileşense, satın almaya razı olduğunuz en büyük makine partition’lı bir alternatifi kurmak için gereken süreden daha uzun bir pay veriyorsa ve tek node üzerindeki transaction semantiği yatay paydan daha değerliyse dikey ölçekleyin. OLTP ilişkisel veritabanları bunun standart örneğidir; Stack Overflow, ana sitelerini uzun yıllar boyunca az sayıda büyük SQL Server makinesinden sunduğunu kamuya açık olarak yazmıştır. McSherry, Isard ve Murray’in Scalability! But at what COST? makalesi, tek makinede çalışan tek thread’li programların yayımlanmış birkaç dağıtık graf işleme sonucunu geride bıraktığını göstermiştir; bu, her cluster’ı en iyi tek makine baz çizgisiyle karşılaştırmak için bir nedendir.

Ölçülen eğri hedef eşzamanlılıkta tepe noktasına yakınsa ya da onu geçmişse, büyüme planlama ufku içinde mevcut en büyük makineyi tüketiyorsa ya da erişilebilirlik hedefi başka bir zone’daki bir standby ile birlikte tek primary ile karşılanamıyorsa dikey ölçeklemeyin.

İstekler bağımsızsa ya da bir partition anahtarı partition’lar arası işlemleri seyrek tutuyorsa, veri hacmi ya da yazma throughput’u tek makineyi aşıyorsa, birden fazla bölgedeki kullanıcıların yerel gecikmeye ihtiyacı varsa ya da verinin belirli yetki alanlarında tutulması gerekiyorsa yatay ölçekleyin. YouTube’un MySQL sharding ihtiyacından doğan Vitess ve partition’ları hem yerleşimin hem de consumer paralelliğinin birimi olan Kafka, ortak işlemleri tek bir partition’a dokunduğu için partition sayısıyla ölçeklenir.

Sıcak bir anahtarı düzeltmek için yatay ölçeklemeyin, çünkü onun trafiği hâlâ tek bir sahibe düşer; replika ekleyerek yazma throughput’unu artırmak için yatay ölçeklemeyin, çünkü her replika aynı işi yapar; doymuş bir veritabanını uygulama instance’ı ekleyerek rahatlatmaya çalışmayın; ve yukarıdaki koşulların hiçbiri yokken spekülatif olarak yatay ölçeklemeyin. İlk günden yatay ölçek için kurulan bir sistem; routing, resharding ve kısmi hata yönetiminin bedelini trafiğin en düşük, iterasyon hızının ise en önemli olduğu dönemde öder. Veri sahipliği net olan bir modüler monolit, bedelini erkenden ödemeden partitioning seçeneğini açık tutar.

  • Gene M. Amdahl, Validity of the Single Processor Approach to Achieving Large Scale Computing Capabilities, AFIPS Spring Joint Computer Conference, 1967.
  • John L. Gustafson, Reevaluating Amdahl’s Law, Communications of the ACM, 1988.
  • Neil J. Gunther, Guerrilla Capacity Planning, Springer, 2007.
  • Paul E. McKenney, Is Parallel Programming Hard, And, If So, What Can You Do About It?, kernel.org (sürekli güncellenir).
  • Jeffrey Dean ve Luiz André Barroso, The Tail at Scale, Communications of the ACM, 2013.
  • Frank McSherry, Michael Isard ve Derek G. Murray, Scalability! But at what COST?, USENIX HotOS, 2015.