Çok iş parçacıklı yüksek eşzamanlılık senaryosunda Java önbellek sorunu nasıl çözülür?

Yazar | LLLSQ

Baş Editör | Guo Rui

İnternet yazılımının hızlı gelişimi, kullanıcı deneyimi bir yazılımın kalitesini değerlendirmek için önemli bir nedendir, bu nedenle önbellek vazgeçilmez bir yapaydır. Çok iş parçacıklı yüksek eşzamanlılık senaryolarında önbellekler genellikle birbirlerinden ayrılamaz.Redis ve memcached gibi dağıtılmış önbellekler ve ehcache, GuavaCache gibi yerel (işlem içi) önbellekler gibi farklı uygulama senaryolarına göre farklı önbelleklerin seçilmesi gerekir. Kafein.

Guava Cache demişken, birçok kişi buna aşinadır. Google Guava araç setinde çok kullanışlı ve kullanımı kolay yerelleştirilmiş bir önbellek uygulamasıdır. LRU algoritmasına dayalı olarak uygulanır ve çoklu önbellek sona erme stratejilerini destekler. Guava'nın yaygın kullanımı nedeniyle, Guava Önbelleği de yaygın olarak kullanılmaktadır. Ancak, Guava Önbelleğinin performansı ille de en iyisi mi? Belki bir kez performansı çok iyidir. Ancak Yangtze Nehri denilen dalgaların dalgaları ileri doğru itmesinden sonra, her zaman daha iyi teknolojiler olacaktır. Bugün, Guava Cache: Caffeine'den daha yüksek performansa sahip bir önbellekleme çerçevesi tanıtacağım.

Resmi performans karşılaştırması

Google Guava araç setinde, LRU algoritmasına dayanan çok kullanışlı ve kullanımı kolay yerelleştirilmiş önbellek uygulaması, birden çok önbellek süre sonu stratejisini destekler.

EhCache, hızlı ve yetenekli olan saf bir Java işlem içi önbellekleme çerçevesidir ve Hazırda Bekletme'de varsayılan CacheProvider'dır.

Caffeine, Spring Boot 2.0'da değiştirilecek olan Java8 kullanılarak Guava önbelleğinin yeniden yazılmış bir sürümüdür.LRU algoritmasına dayalı olarak uygulanır ve çoklu önbellek sona erme stratejilerini destekler.

  • Senaryo 1: 8 iş parçacığı okundu,% 100 okuma işlemi.

  • Senaryo 2: 6 iş parçacığı okuma, 2 iş parçacığı yazma, yani% 75 okuma işlemi ve% 25 yazma işlemi.

  • Senaryo 3: 8 iş parçacığı yazma,% 100 yazma işlemi.

Kafeinin diğer önbelleklerden önemli ölçüde daha verimli olduğu açıkça görülebilir.

nasıl kullanılır?

1 genel statik boşluk ana (String args) { 2 LoadingCache < Dize, Dize > build = CacheBuilder.newBuilder (). initialCapacity (1) .maximumSize (100) .expireAfterWrite (1, TimeUnit.DAYS) 3. .build (yeni CacheLoader < Dize, Dize > () { 4 // Varsayılan veri yükleme uygulaması, değeri almak için get çağrılırken, anahtarın karşılık gelen bir değeri yoksa, yüklemek için bu yöntemi çağırın 5 @Override 6 public String yükü (String key) { 7 dönüş ""; 8} 9}); 10} 11}

Parametre yöntemi:

  • initialCapacity (1) İlk arabellek uzunluğu 1'dir;
  • maximumSize (100) Maksimum uzunluk 100'dür;
  • expireAfterWrite (1, TimeUnit.DAYS) Önbellek politikasını süresi dolan önbelleğe 1 gün içinde yazmayacak şekilde ayarlayın (önbellek politikası daha sonra tartışılacaktır).

Son kullanma stratejisi

Kafeinde iki tür önbellek vardır, biri sınırlı önbellek ve diğeri sınırsız önbellektir Sınırsız önbelleğin süresinin dolmasına gerek yoktur ve sınırları yoktur.

Sınırlı önbellekte üç süre sonu API'si sağlanır:

  • expireAfterWrite: Yazdıktan sonra ne kadar süre dolduğunu gösterir. (Yukarıdaki Liezi bu yoldur)
  • expireAfterAccess: son ziyaretten sonra süresinin dolduğunu gösterir.
  • expireAfter: expireAfter'da Expiry arayüzünü kendiniz uygulamanız gerekir.Bu arayüz oluşturma, güncelleme ve erişimden sonra ne kadar süre dolacağını destekler. Bu API ile önceki iki API'nin birbirini dışladığına dikkat edin. Bu ve önceki iki API arasındaki fark, önbelleğe alma çerçevesine belirli bir zamanda sona ermesi gerektiğini söylemeniz gerektiğidir, yani önceki yeniden yazma, güncelleme ve erişim yöntemleriyle belirli bir sona erme süresini elde etmek için.

Stratejiyi güncelle

Güncelleme stratejisi nedir? Önbellek, ayarlanan sürenin ardından otomatik olarak yenilenir.

Kafein, yazdıktan sonra stratejiyi güncellememize izin veren yenilemeAfterWrite () yöntemini sağlar:

1 Yükleme Önbelleği < Dize, Dize > build = CacheBuilder.newBuilder (). renewAfterWrite (1, TimeUnit.DAYS) 2.build (yeni CacheLoader < Dize, Dize > () { 3 @Override 4 public String load (String key) { 5 dönüş ""; 6} 7}); 8}

Yukarıdaki kodda, yenilemek için bir CacheLodaer oluşturmamız gerekiyor, burada senkron olarak yapılır ve buildAsync yöntemi ile zaman uyumsuz olarak oluşturulabilir. Gerçek işte, veri kaynağını yenilemek için kodumuzdaki eşleyiciyi geçirebiliriz.

Ancak gerçek kullanımda, yenilenmesi için bir gün ayarlarsınız, ancak bir gün sonra önbelleğin yenilenmediğini görürsünüz. Bunun nedeni, önbelleğin ancak tekrar erişildikten 1 gün sonra yenilenebilmesidir, eğer kimse erişmezse, asla yenilenmeyecektir.

Otomatik yenilemenin nasıl yapıldığına bir göz atalım. Otomatik yenileme, yalnızca afterRead () yöntemimiz olan okuma işleminden sonra mevcuttur.Bunlardan biri, eşzamanlı veya eşzamansız olmanıza göre yenilenecek olan renewIfNeeded'dir.

Doldurma stratejisi (Nüfus)

Kafein bize üç doldurma stratejisi sağlar: manuel, senkronize ve asenkron.

Manuel yükleme (Manuel)

1 Önbellek < Dize, Nesne > manualCache = Kafein.newBuilder () 2.expireAfterWrite (10, TimeUnit.MINUTES) 3. maksimumBoyut (10_000) 4.. Yapı (); 56String key = "name1"; 7 // NULL döndürmezse, anahtara göre bir önbellek sorgulayın 8graph = manualCache.getIfPresent (anahtar); 9 // createExpensiveGraph yöntemi çağrılmamışsa, Anahtarı temel alan bir önbellek sorgulayın ve dönüş değerini önbelleğe kaydedin. 10 // Eğer yöntem Null döndürürse, manualCache.get null döndürür, eğer yöntem bir istisna atarsa, manualCache.get bir istisna atar 11graph = manualCache.get (anahtar, k- > createExpensiveGraph (k)); 12 // Önbelleğe bir değer koy, daha önce bir değer varsa, önceki değerin üzerine yaz 13manualCache.put (anahtar, grafik); 14 // Bir önbelleği silin 15manualCache.invalidate (anahtar); 1617 Eşzamanlı Harita < Dize, Nesne > map = manualCache.asMap (); 18cache.invalidate (anahtar);

Önbellek arayüzü, önbellek alma, güncelleme ve silme işlemlerinin açık bir şekilde kontrol edilmesini sağlar. Bir anahtarın değerini cache.getIfPresent (key) yöntemi ile alabiliriz ve CNC'yi cache.put (key, value) yöntemi ile önbelleğe koyabiliriz, ancak bu orijinal anahtarın verilerinin üzerine yazacaktır. Cache.get (key, k- > değer), get yöntemi parametre olarak parametre olarak bir anahtarla bir Function (createExpensiveGraph) iletir.

Anahtar önbellekte yoksa, bu Function işlevini çağırın ve dönüş değerini önbelleğin değeri olarak önbelleğe ekleyin. Get yöntemi çağrıyı engelleyici bir şekilde yürütür.Çok sayıda iş parçacığı değeri aynı anda talep etse bile, Function yöntemi yalnızca bir kez çağrılacaktır. Bu, diğer iş parçacıklarıyla yazma rekabetini önleyebilir, bu nedenle get kullanımı getIfPresent'ten daha iyidir.

Not: Bu yöntemi çağırmak NULL döndürürse (yukarıdaki createExpensiveGraph yöntemi gibi), cache.get null döndürür. Bu yöntemi çağırmak bir istisna atarsa, get yöntemi de bir istisna atar.

ConcurrentMap'i almak ve önbellekte bazı değişiklikler yapmak için Cache.asMap () yöntemini kullanabilirsiniz.

Eşzamanlı yükleme (Yükleme)

1Yükleniyor Önbellek < Dize, Nesne > loadingCache = Kafein.newBuilder () 2. maksimumBoyut (10_000) 3.expireAfterWrite (10, TimeUnit.MINUTES) 4. yapı (anahtar- > createExpensiveGraph (anahtar)); 56String key = "name1"; 7 // Eşzamanlı bir şekilde bir önbellek elde etmekle aynı prensip ve yukarıdaki manuel yolla. Önbellek oluştururken bir createExpensiveGraph işlevi sağlanacaktır. 8 // Eksik olması durumunda bir önbellek oluşturmak için senkronizasyonu sorgulayın ve kullanın 9Nesne grafiği = loadingCache.get (anahtar); 1011 // Grup anahtarının değerini al ve bir Harita döndür 12Liste < Dize > anahtarlar = new ArrayList < > (); 13keys.add (anahtar); 14Harita < Dize, Nesne > graphs = loadingCache.getAll (anahtarlar);

LoadingCache, CacheLoader kullanılarak oluşturulan önbelleğin değeridir. Toplu arama için getAll yöntemini kullanabilirsiniz. Varsayılan olarak getAll, önbelleğe alınan değeri oluşturmak için önbellekte değeri olmayan anahtarlar üzerinde CacheLoader.load yöntemini çağırır. GetAll verimliliğini artırmak için CacheLoader.loadAll yöntemini geçersiz kılabiliriz.

Not: Özel olarak istenen anahtarın değerlerini yüklemek için bir CacheLoader.loadAll yazabilirsiniz. Örneğin, belirli bir gruptaki herhangi bir anahtarın değerini hesaplamak, o gruptaki tüm anahtarlar için değerler sağlayacaksa, loadAll, grubun geri kalanını aynı anda yükleyebilir.

Eşzamansız Yükleniyor

1AsyncLoadingCache < Dize, Nesne > asyncLoadingCache = Caffeine.newBuilder () 2. maksimumBoyut (10_000) 3.expireAfterWrite (10, TimeUnit.MINUTES) 4 // İkisi de: Eşzamansız olarak sarmalanmış eşzamanlı bir hesaplamayla derleyin 5 .buildAsync (anahtar- > createExpensiveGraph (anahtar)); 6 // Veya: Geleceği döndüren eşzamansız bir hesaplamayla derleyin 7 // .buildAsync ((anahtar, yürütücü) - > createExpensiveGraphAsync (anahtar, yürütücü)); 89 Dize anahtarı = "ad1"; 1011 // Eksik olması durumunda önbelleği oluşturmak için eşzamansız yöntemleri sorgulayın ve kullanın 12Tamamlanabilir Gelecek < Nesne > graph = asyncLoadingCache.get (anahtar); 13 // Bir dizi önbelleği sorgulayın ve eksik olması durumunda önbelleği oluşturmak için zaman uyumsuz yöntemler kullanın 14Liste < Dize > anahtarlar = new ArrayList < > (); 15keys.add (anahtar); 16Tamamlanabilir Gelecek < Harita < Dize, Nesne > > graphs = asyncLoadingCache.getAll (anahtarlar); 17 // Eşzamansızdan eşzamanlıya 18loadingCache = asyncLoadingCache.synchronous ();

AsyncLoadingCache, LoadingCache sınıfından miras alınır. Zaman uyumsuz yükleme, yöntemleri çağırmak ve CompletableFuture döndürmek için Executor'u kullanır. Eşzamansız yükleme önbelleği, reaktif bir programlama modeli kullanır.

Eşzamanlı olarak aramak istiyorsanız, CacheLoader sağlamalısınız. Eşzamansız olarak ifade etmek için bir AsyncCacheLoader sağlanmalı ve bir CompletableFuture döndürülmelidir.

Senkron () yöntemi bir LoadingCacheView görünümü döndürür ve LoadingCacheView ayrıca LoadingCache'den devralır. Bu yöntemin çağrılması, zaman uyumsuz olarak yüklenen bir önbelleği AsyncLoadingCache'yi zaman uyumlu olarak yüklenmiş bir önbellek LoadingCache'ye dönüştürmeye eşdeğerdir.

Varsayılan olarak, ForkJoinPool.commonPool () eşzamansız evreleri çalıştırmak için kullanılır, ancak iş parçacığı havuzunu değiştirmek için Caffeine.executor (Executor) yöntemini kullanabiliriz.

Tahliye stratejisi (tahliye)

Kafein üç tür tahliye stratejisi sağlar: boyuta dayalı, zamana dayalı ve referansa dayalı.

Beden bazlı

Boyut tahliyesine bağlı olarak iki yol vardır: biri önbellek boyutuna, diğeri ağırlığa bağlıdır.

1 // Önbellekteki giriş sayısına göre tahliye edin 2 // Önbellek sayısına göre tahliye 3Yükleniyor Önbellek < Anahtar, Grafik > grafikler = Kafein.newBuilder () 4. maksimumBoyut (10_000) 5. yapı (anahtar- > createExpensiveGraph (anahtar)); 67 // Önbellekteki köşe sayısına göre tahliye edin 8 // Tahliye, önbelleğin ağırlığına bağlıdır (ağırlık, önbelleğin çıkarılıp çıkarılmadığını belirlemek için değil, yalnızca önbelleğin boyutunu belirlemek için kullanılır) 9Yükleniyor Önbellek < Anahtar, Grafik > grafikler = Kafein.newBuilder () 10. maksimumAğırlık (10_000) 11. tartı ((Anahtar anahtar, Grafik grafiği) - > graph.vertices (). size ()) 12. yapı (anahtar- > createExpensiveGraph (anahtar));

Önbelleğin maksimum boyutunu belirtmek için Caffeine.maximumSize (long) yöntemini kullanabiliriz. Önbellek bu kapasiteyi aştığında, önbelleği silmek için Window TinyLfu stratejisi kullanılacaktır. Ayrıca boşaltmak için ağırlık stratejisini kullanabiliriz, ağırlığı belirlemek için Caffeine.weigher (Tartı) işlevini ve önbelleğin maksimum ağırlığını belirtmek için Caffeine.maximumWeight (uzun) işlevini kullanabiliriz.

Not: MaximumWeight ve maximumSize aynı anda kullanılamaz.

Zamana dayalı

1 // Sabit bir sona erme politikasına göre tahliye edin 2 // Sabit bir sona erme stratejisine dayalı çıkış 3Yükleniyor Önbellek < Anahtar, Grafik > grafikler = Kafein.newBuilder () 4.expireAfterAccess (5, TimeUnit.MINUTES) 5. yapı (anahtar- > createExpensiveGraph (anahtar)); 6Yükleniyor Önbellek < Anahtar, Grafik > grafikler = Kafein.newBuilder () 7.expireAfterWrite (10, TimeUnit.MINUTES) 8. yapı (anahtar- > createExpensiveGraph (anahtar)); 910 // Değişen bir sona erme politikasına göre tahliye edin 11 // Farklı sona erme stratejilerine göre çıkış 12Yükleniyor Önbellek < Anahtar, Grafik > grafikler = Kafein.newBuilder () 13. expireAfter (yeni Geçerlilik < Anahtar, Grafik > () { 14 @Override 15 genel uzun süreli expireAfterCreate (Anahtar anahtarı, Grafik grafiği, uzun currentTime) { 16 // Harici bir kaynaktan geliyorsa, nanotime yerine duvar saati zamanını kullan 17 uzun saniye = graph.creationDate (). PlusHours (5) 18 .minus (System.currentTimeMillis (), MILLIS) 19. toEpochSecond (); 20 dönüş TimeUnit.SECONDS.toNanos (saniye); yirmi bir } yirmi iki 23 @Override 24 halka açık uzun süreli sona ermeAfterUpdate (Anahtar anahtar, Grafik grafik, 25 uzun currentTime, long currentDuration) { 26 dönüş güncelSüresi; 27} 2829 @Override 30 halka açık uzun süre sona erdiAfterRead (Anahtar anahtar, Grafik grafik, 31 uzun currentTime, long currentDuration) { 32 dönüş güncel süresi; 33} 34}) 35. Yapı (anahtar- > createExpensiveGraph (anahtar));

Referans tabanlı

Yukarıdan aşağıya Java 4 referanslarının seviyesi: güçlü referanslar > Yumuşak referans > Zayıf referans > Hayali referans.

1 // Ne anahtara ne de değere güçlü bir şekilde ulaşılamadığında tahliye edin 2 // Ne anahtara ne de değere başvurulmadığında önbelleği çıkarın 3Yükleniyor Önbellek < Anahtar, Grafik > grafikler = Kafein.newBuilder () 4. .weakKeys () 5 .weakValues () 6. yapı (anahtar- > createExpensiveGraph (anahtar)); 78 // Çöp toplayıcının belleği boşaltması gerektiğinde tahliye edin 9 // Çöp toplayıcının belleği boşaltması gerektiğinde çıkarılır 10Yükleniyor Önbellek < Anahtar, Grafik > grafikler = Kafein.newBuilder () 11. softValues () 12. yapı (anahtar- > createExpensiveGraph (anahtar));

Önbellek tahliyesini çöp toplayıcıya göre yapılandırabiliriz. Bu amaçla, anahtar ve değeri zayıf referanslar olarak veya yalnızca değerleri yumuşak referanslar olarak yapılandırabiliriz.

Not: AsyncLoadingCache, zayıf referansları ve yazılım referanslarını desteklemez.

Dinleyiciyi kaldırın (Kaldırma)

  • Tahliye (tahliye): Belli bir tahliye stratejisi karşılandığından, silme işlemi arka planda otomatik olarak gerçekleştirilir;
  • Geçersiz kılma: arayanın önbelleği manuel olarak sileceği anlamına gelir;
  • Kaldırma: Çıkarma veya geçersiz işlemleri izleyen bir dinleyici.

Önbelleği manuel olarak silin:

Herhangi bir zamanda, önbelleğin çıkarılmasını beklemeden önbelleği açıkça geçersiz kılmak mümkündür.

1 // bireysel anahtar 2cache.invalidate (anahtar) 3 // toplu anahtarlar 4cache.invalidateAll (anahtarlar) 5 // tüm anahtarlar 6cache.invalidateAll ()

Kaldırma işleyici:

Caffeine.removalListener (RemovalListener), verileri silerken belirli işlemleri gerçekleştirmek üzere önbellek için bir silme dinleyicisi belirtmek için kullanılabilir. RemovalListener anahtarı, değeri ve RemovalCause'u (silme nedeni) alabilir.

Silme dinleyicisinin içindeki işlem Executor kullanılarak eşzamansız olarak gerçekleştirilir. Varsayılan yürütücü, Caffeine.executor (Executor) tarafından geçersiz kılınabilir ForkJoinPool.commonPool () 'dur. İşlemin silme ile eşzamanlı olarak gerçekleştirilmesi gerektiğinde, lütfen bunun yerine CacheWrite kullanın.Aşağıda CacheWrite açıklanacaktır.

Not: RemovalListener tarafından atılan herhangi bir istisna günlüğe kaydedilecek (Logger kullanılarak) ve atılmayacaktır.

İstatistikler (İstatistikler)

Önbellek < Anahtar, Grafik > grafikler = Kafein.newBuilder () 2. maksimumBoyut (10_000) 3. .recordStats () 4.. Yapı ();

İstatistik toplamayı açmak için Caffeine.recordStats () kullanın. Cache.stats () yöntemi, aşağıdakiler gibi istatistiksel bilgiler sağlayan CacheStats döndürür:

  • hitRate (): isabetlerin isteklere oranını döndürür;
  • hitCount (): önbellekteki toplam isabet sayısını döndürür;
  • evictionCount (): önbellek tahliyelerinin sayısı;
  • averageLoadPenalty (): Yeni bir değerin yüklenmesi için geçen ortalama süredir.

sonuç olarak

Kafein ayarlaması sadece algoritmanın ayarlanması değil, aynı zamanda hafızanın optimizasyonudur.Kafeine API'sinin çalışma işlevi temelde Guava'nınkiyle aynıdır ve Kafein daha önce Guava kullanıcısı ile uyumludur, bu nedenle önbelleği kullanmak veya yeniden yazmak için Kafein sorun olmamalı ama aynı zamanda projeye de bağlı, körü körüne kullanmayın.

Yazar: LLLSQ, bir North drift programcılarının trajik bir öyküsüne sahip, BT ve teknik makaleler gerçek zamanlı okuyucularda sıcak haberler, mimari, röportajlar ve diğer en son bilgiler.

Feragatname: Bu makale yazar tarafından sunulmuştur ve telif hakkı karşı tarafa aittir.

Gökyüzüne meydan okuyan bu motor teknolojilerini anlayın, şarap masasının kralı sizsiniz
önceki
Laixi kamu güvenlik organları, 5 temel göreve saldırı başlatmak için 2 "ön kuvvet" kurdu
Sonraki
İlk kez ortaya çıkan yeni BMW 1 serisi iç mekan casus fotoğrafları çok keskin oluyor
En çok parayı hangi Python programcısı kazanıyor?
2017'de Hangzhou'nun Animasyon ve Oyun Endüstrisi Gelişimindeki On Önemli Nokta
Pandalar SQL'de nasıl açılır?
100 gün geri sayım, Cai Qi bu büyük etkinlik için bir sprint seferberlik emri yayınladı
Samsung'un ilk "delikli ekran" cep telefonu burada: çentikli ekranı ve su damlası ekranını karşılaştırın
2019 Tianheng Festivali Deniz Festivali burada! 3 ana bölüm ve 6 ana vurgu. Kılavuz gönderilir ~
Pekin'in ilk "Air Surge" si açıldı ve popüler eğlence sporları bir arada sunuldu
Devlet İlaç İdaresi hatırlatıyor! Bu ilaçlar çocuklar için yasaktır! Ebeveynler görmeli!
"Ant-Man 2" de izlenecek bir şey olup olmadığını görmek için 500 Douban film incelemesini taramak için Python kullanın.
Tanınmış oyuncular ve oyuncu yönetmenleri "ulusal seçmelere" katılıyor! "Grup Performansı Komünü", hayallerin peşinden gitmek için grup performansını teşvik eder
Kıdemli Alman hayranı, yeni Audi A6L'den bahsediyor, bir görev vermek zorunda
To Top