Dört Adımlık Desen
Eksiklik dört sıra dışı, atomik olmayan adimde yaşıyor:
// EKSİKLİK
Değer value = cache.get(key);
if (value == null) {
value = pahalıCompute(key);
cache.put(key, value);
}
return value;
Adım 1: önbellek kontrolü. Adım 2: misses. Adım 3: hesapla. Adım 4: sakla. Dört adım: tümü atomik olmayan. Adım 1 ve 4 arasında, herhangi bir sayıdaki thread Adım 1'i çalıştırabilir ve hepsi null görür.
İdempotanslık Tuzağı
Bu deseni koruyan düşünce: 'iki thread aynı değeri hesaplayıp kaydedebilir. Sonuç idempotent. Veri bozulması olmaz.'
Bu düşünce: doğruluk konusunda doğru. Ama maliyet konusunda ölümcül.
1.000 thread üzerinde, önbellek missesi: Her thread her biri için expensiveCompute(key) çalıştırır. Eğer expensiveCompute bir veritabanı sorgusu yaparsa, 1.000 tane aynı anda veritabanı sorgusu gönderir. Eğer dış bir hizmet çağrıyorsa, 1.000 tane aynı anda HTTP isteği gönderir. Sistem, sonuçları üretirken maliyeti alt üst olur.
Üç Tetikleyici
Bir Gürlemesi Ova, önbellek anahtarının sıcaklıkten soğukluğa aynı anda birçok thread üzerinde geçtiği anda ateşlenir:
Soğuk başlangıç: hizmet, boş bir önbellekle yeniden başlatılır. İlk talep dalgası: her anahtar misses. Tüm hesaplar aynı anda yapılır.
Hizmet yeniden başlatma: döner yeniden başlatma, örnekler arası önbelleği sıfırlar. Trafiği soğuk örnekler arasında yeniden dağıtır.
TTL süresi geçme: yüksek trafiğe sahip bir anahtar süresiz olur. N thread hepsi kontrol eder, hepsi misses, en az bir thread sonucunu kaydederken diğerleri de hesaplar.
Üç tüm tetikleyici: trafiğe patlamalarla ilişkilidir. Sürü, trafik zirve yaptığı ve önbellek soğuk olduğu anda ateşlenir. En kötü zaman.
Elasticsearch EnrichCache Örneği
Elasticsearch EnrichCache: belgili yorum, 'basitlik için kasıtsız olarak kilit açıklığa sahip... Aynı anda aynı anahtar/değer çiftini yarış koşuluyla tekrar kaydetmeye izin verir.' 10.000 belge/saniye ile soğuk bir zenginleştirmeli önbellekte: tüm 10.000 talep zengin indekse aynı anda hits yapar. Zengin indeks, nadir sorgular için tasarlanmıştır ve 10.000 paralel sorguya maruz kalır. Kümeler destabilizes.
İdempotanslık düşünce: kod yorumunda doğru. 10.000 belge/saniye durumunda felaket olur.
MOAD-0001 ile Bağımlılık
MOAD-0001 (Kayaç Defekti) yüksek işleme sistemlerinde O(N²) bir boğucu oluşturur. MOAD-0001'i (O(N²)dan O(N)'ye) düzeltmek bu iş istasyonunu açar. Hızlı işleme daha fazla talep gönderir. Daha önceki korunan MOAD-0001 boğucusu olmadan, alt akış depolarda korelasyonlu trafik patlamaları alır. MOAD-0005, önce tetiklenmemiş depolarda ateşler. Bir MOAD'ı düzelt; diğerini sahnelleştir.
Nesne Sabitleme Püskürtüsünün Yanlışlarını Anlama
Elasticsearch yorumu, hatalı soruya uygulanan dikkatli mühendislik analizi temsil eder. Nesne sabitlenlilik: Gerçekten düşünülmesi değer olan bir özelli. Püskürtü: Analiz çerçevesini durdurma hatalı mod (hata yerine maliyet).
computeIfAbsent & singleflight
Çözüm: Kontrol ve hesaplamayı atomik hale getirin. Bir dizi hesaplar. Diğer tüm diziler o sonucun için bekler.
Java: computeIfAbsent
// DEFECT: dört atomik olmayan adım
Value value = cache.get(key);
if (value == null) {
value = expensiveCompute(key);
cache.put(key, value);
}
return value;
// FIX: atomik kontrol ve hesaplanma
return cache.computeIfAbsent(key, k -> expensiveCompute(k));
computeIfAbsent: eğer anahtar eksikse, bir kez hesaplanır, depolanır, geri döner. Diğer tüm döngüler computeIfAbsent'i aynı anahtar için çağırırken, ilk hesaplamayı beklerler. N katlı hesaplamalar yoktur. Çıkanlar yok.
Go: singleflight.Group
var g singleflight.Group
func getOrCompute(key string) (Value, error) {
v, err, _ := g.Do(key, func() (interface{}, error) {
return expensiveCompute(key)
})
return v.(Value), err
}
singleflight: eğer bir anahtar için bir hesaplama zaten çalışıyorsa, aynı anahtar için çağırıcılar bekler ve tek sonuçları paylaşır. Bir hesaplama, N bekleme, tek bir sonuç paylaşılır. 'flight' soyutlaması: uçarken düşenleri azaltmak.
Mutex vs singleflight
Gayri saf bir anahtar bazında mutex, thread 1 hesaplar, thread 2 bekler, thread 3 bekler. Thread 1 bitirdikten sonra, thread 2 girer ve cache'yi (hit) kontrol eder. Thread 3 girer ve cache'yi (hit) kontrol eder. N-1 mutex alma ve cache okuma işlemi.
singleflight, thread 1 hesaplar, thread 2'den thread N tüm thread 1'in sonucunu bekler. Ayırca mutex alma ve cache okuma işlemi yoktur. Bir hesap, bir sonuç, N bekleme. Mutex'ten daha az işlem.
Her ikisi de basıncı önler. singleflight, daha tamamen yinelenen işleri önlemeye yarar.
Dizin Kalıbını Yeniden Yazın
Somut bir senaryoya uygulayın.
// Yüksek trafiğe sahip bir Java hizmeti olan bir kullanıcı profili cache'si
public UserProfile getProfile(String userId) {
UserProfile profile = profileCache.get(userId);
if (profile == null) {
profile = database.loadProfile(userId); // pahalı: 50ms DB sorgusu
profileCache.put(userId, profile);
}
return profile;
}
Hizmet her gün 2 AM'de yeniden başlatır. 8 AM'de 10.000 kullanıcı profillerini aynı anda istediklerinde.