2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
Java-maailmassa välimuistitekniikkaa käytetään laajalti sovellusten suorituskyvyn parantamiseen, ja se jaetaan pääasiassa kahteen luokkaan: etävälimuisti ja paikallinen välimuisti. Erinomaisen suorituskyvyn ja joustavuuden ansiosta etävälimuisti on usein toteutettu suosituilla ratkaisuilla, kuten Redis ja Memcached. Paikallista välimuistia, jolla on kevyt ja nopea pääsy, edustavat tekniikat, kuten HashMap, Guava Cache, Caffeine ja Ehcache.
Syvennymme etävälimuistin mysteereihin tulevissa blogikirjoituksissa, mutta tänään keskitytään paikalliseen välimuistiin. Tässä artikkelissa käydään ensin läpi paikallisen välimuistitekniikan, jotta saat kattavan yleiskatsauksen. Seuraavaksi perehdymme suorituskyvyn kuninkaaksi kutsuttuun välimuistitekniikkaan ja tutkimme sen taustalla olevia periaatteita ja toteutustapoja. Lopuksi osoitamme useiden käytännön tapausten avulla, kuinka voit tehokkaasti hyödyntää näitä korkean suorituskyvyn paikallisia välimuistitekniikoita päivittäisessä työssäsi kehitystehokkuuden ja sovellusten suorituskyvyn parantamiseksi.
Mapin taustalla olevan toteutuksen avulla voimme tallentaa välimuistiin tallennettavat kohteet suoraan muistiin, mikä on suora ja tehokas tapa.
Etu: Tämä menetelmä on yksinkertainen ja suora, ilman ulkoisia kirjastoja, ja se sopii erittäin hyvin sovelluksiin, joissa on mutkaton välimuistivaatimukset ja yksinkertaiset skenaariot.
Haitat: Tästä menetelmästä puuttuu kuitenkin automaattinen välimuistin poistomekanismi. Jos tarvitaan edistyneempiä välimuististrategioita, voidaan tarvita korkeampia mukautettuja kehityskustannuksia.
- public class LRUCache extends LinkedHashMap {
-
- /**
- * 可重入读写锁,保证并发读写安全性
- */
- private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
- private Lock readLock = readWriteLock.readLock();
- private Lock writeLock = readWriteLock.writeLock();
-
- /**
- * 缓存大小限制
- */
- private int maxSize;
-
- public LRUCache(int maxSize) {
- super(maxSize + 1, 1.0f, true);
- this.maxSize = maxSize;
- }
-
- @Override
- public Object get(Object key) {
- readLock.lock();
- try {
- return super.get(key);
- } finally {
- readLock.unlock();
- }
- }
-
- @Override
- public Object put(Object key, Object value) {
- writeLock.lock();
- try {
- return super.put(key, value);
- } finally {
- writeLock.unlock();
- }
- }
-
- @Override
- protected boolean removeEldestEntry(Map.Entry eldest) {
- return this.size() > maxSize;
- }
- }
Guava Cache on Googlen kehittämä välimuistitekniikka, joka perustuu LRU-korvausalgoritmiin (vihiten käytetty). Kofeiinin nousun myötä Guava Cache kuitenkin katosi vähitellen ihmisten näkyvistä. Kofeiini ei vain peri Guava Cachen edut, vaan myös ylittää sen monin tavoin. Vaikka tässä ei anneta erityistä esimerkkikoodia, Guava-välimuistista kiinnostuneet lukijat voivat vierailla sen virallisella verkkosivustolla saadaksesi lisätietoja.
Etu: Guava-välimuisti tukee enimmäiskapasiteettirajojen asettamista ja tarjoaa kaksi vanhenemisstrategiaa: perustuen lisäysaikaan ja käyttöaikaan, ja tukee myös joitain tilastollisia perustoimintoja.
Haitat: Spring Boot 2:n ja Spring 5:n julkaisun myötä Guava Cachea ei enää suositella molemmille.
Kofeiini on avoimen lähdekoodin välimuistitekniikka, joka käyttää W-TinyLFU-algoritmia, joka on välimuistin poistostrategia, jossa yhdistyvät LRU:n ja LFU:n (vähiten käytetty taajuus) edut. Kofeiinin välimuistin suorituskyky on lähellä teoreettista optimaalista ratkaisua ja sitä voidaan pitää Guava Cachen päivitettynä versiona.
- public class CaffeineCacheTest {
-
- public static void main(String[] args) throws Exception {
- //创建guava cache
- Cache<String, String> loadingCache = Caffeine.newBuilder()
- //cache的初始容量
- .initialCapacity(5)
- //cache最大缓存数
- .maximumSize(10)
- //设置写缓存后n秒钟过期
- .expireAfterWrite(17, TimeUnit.SECONDS)
- //设置读写缓存后n秒钟过期,实际很少用到,类似于expireAfterWrite
- //.expireAfterAccess(17, TimeUnit.SECONDS)
- .build();
- String key = "key";
- // 往缓存写数据
- loadingCache.put(key, "v");
-
- // 获取value的值,如果key不存在,获取value后再返回
- String value = loadingCache.get(key, CaffeineCacheTest::getValueFromDB);
-
- // 删除key
- loadingCache.invalidate(key);
- }
-
- private static String getValueFromDB(String key) {
- return "v";
- }
- }
Ehcache on puhdas Java-prosessin sisäinen välimuistikehys, joka tunnetaan nopeasta ja tehokkaasta suorituskyvystään. Se on laajalti tunnustettu ja toimii oletusvälimuistin tarjoajana Hibernatessa.
Etu : Ehcache tarjoaa laajan valikoiman välimuistin häätöalgoritmeja, mukaan lukien LFU (vähiten käytetty), LRU (vihiten käytetty) ja FIFO (first in, first out). Se tukee erilaisia välimuistityyppejä, kuten in-heap-välimuistia, off-heap-välimuistia ja levyvälimuistia, mukautuakseen erilaisiin tallennustarpeisiin. Lisäksi Ehcache tukee myös erilaisia klusteriratkaisuja, jotka ratkaisevat tehokkaasti tiedon jakamisen ongelman.
Haitat: Vaikka Ehcache on monin tavoin erinomaista, se jää suorituskyvyltään hieman alle kofeiinin.
-
- public class EncacheTest {
-
- public static void main(String[] args) throws Exception {
- // 声明一个cacheBuilder
- CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
- .withCache("encacheInstance", CacheConfigurationBuilder
- //声明一个容量为20的堆内缓存
- .newCacheConfigurationBuilder(String.class,String.class, ResourcePoolsBuilder.heap(20)))
- .build(true);
- // 获取Cache实例
- Cache<String,String> myCache = cacheManager.getCache("encacheInstance", String.class, String.class);
- // 写缓存
- myCache.put("key","v");
- // 读缓存
- String value = myCache.get("key");
- // 移除换粗
- cacheManager.removeCache("myCache");
- cacheManager.close();
- }
- }
Cache<Key, Graph> cache = Caffeine.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTES)
.maximumSize(10_000)
.build();
// 查找一个缓存元素, 没有查找到的时候返回null
Graph graph = cache.getIfPresent(key);
// 查找缓存,如果缓存不存在则生成缓存元素, 如果无法生成则返回null
graph = cache.get(key, k -> createExpensiveGraph(key));
// 添加或者更新一个缓存元素
cache.put(key, graph);
// 移除一个缓存元素
cache.invalidate(key);
Välimuistin käyttöliittymä tarjoaa mahdollisuuden etsiä, päivittää ja poistaa välimuistissa olevia elementtejä käyttämällä eksplisiittisiä hakuja. Kun välimuistissa olevaa elementtiä ei voida luoda tai generointiprosessin aikana tehdään poikkeus ja elementin luominen epäonnistuu, cache.get voi palauttaa null -arvon.
LoadingCache<Key, Graph> cache = Caffeine.newBuilder()
.maximumSize(10_000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build(key -> createExpensiveGraph(key));
// 查找缓存,如果缓存不存在则生成缓存元素, 如果无法生成则返回null
Graph graph = cache.get(key);
// 批量查找缓存,如果缓存不存在则生成缓存元素
Map<Key, Graph> graphs = cache.getAll(keys);
LoadingCache on välimuistin toteutus, johon on lisätty CacheLoader-ominaisuus.
Jos välimuistia ei ole, vastaava välimuistielementti luodaan CacheLoader.loadin kautta.
AsyncCache<Key, Graph> cache = Caffeine.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTES)
.maximumSize(10_000)
.buildAsync();
// 查找一个缓存元素, 没有查找到的时候返回null
CompletableFuture<Graph> graph = cache.getIfPresent(key);
// 查找缓存元素,如果不存在,则异步生成
graph = cache.get(key, k -> createExpensiveGraph(key));
// 添加或者更新一个缓存元素
cache.put(key, graph);
// 移除一个缓存元素
cache.synchronous().invalidate(key);
AsyncCache on välimuistin asynkroninen muoto, joka tarjoaa Executorille mahdollisuuden luoda välimuistielementtejä ja palauttaa CompletableFuturen. Oletussäievaraston toteutus on ForkJoinPool.commonPool(), mutta voit myös mukauttaa säieryhmän valintaa ohittamalla ja toteuttamalla Caffeine.executor(Executor)-menetelmän.
AsyncLoadingCache<Key, Graph> cache = Caffeine.newBuilder()
.maximumSize(10_000)
.expireAfterWrite(10, TimeUnit.MINUTES)
// 你可以选择: 去异步的封装一段同步操作来生成缓存元素
.buildAsync(key -> createExpensiveGraph(key));
// 你也可以选择: 构建一个异步缓存元素操作并返回一个future
.buildAsync((key, executor) -> createExpensiveGraphAsync(key, executor));
// 查找缓存元素,如果其不存在,将会异步进行生成
CompletableFuture<Graph> graph = cache.get(key);
// 批量查找缓存元素,如果其不存在,将会异步进行生成
CompletableFuture<Map<Key, Graph>> graphs = cache.getAll(keys);
AsyncLoadingCache on LoadingCachen asynkroninen muoto, joka tarjoaa asynkronisen latauksen toiminnon välimuistielementtien luomiseksi.
// 基于缓存内的元素个数进行驱逐
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.maximumSize(10_000)
.build(key -> createExpensiveGraph(key));
// 基于缓存内元素权重进行驱逐
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.maximumWeight(10_000)
.weigher((Key key, Graph graph) -> graph.vertices().size())
.build(key -> createExpensiveGraph(key));
// 基于固定的过期时间驱逐策略
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.expireAfterAccess(5, TimeUnit.MINUTES)
.build(key -> createExpensiveGraph(key));
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTES)
.build(key -> createExpensiveGraph(key));
// 基于不同的过期驱逐策略
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.expireAfter(new Expiry<Key, Graph>() {
public long expireAfterCreate(Key key, Graph graph, long currentTime) {
// Use wall clock time, rather than nanotime, if from an external resource
long seconds = graph.creationDate().plusHours(5)
.minus(System.currentTimeMillis(), MILLIS)
.toEpochSecond();
return TimeUnit.SECONDS.toNanos(seconds);
}
public long expireAfterUpdate(Key key, Graph graph,
long currentTime, long currentDuration) {
return currentDuration;
}
public long expireAfterRead(Key key, Graph graph,
long currentTime, long currentDuration) {
return currentDuration;
}
})
.build(key -> createExpensiveGraph(key));
// 当key和缓存元素都不再存在其他强引用的时候驱逐
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.weakKeys()
.weakValues()
.build(key -> createExpensiveGraph(key));
// 当进行GC的时候进行驱逐
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.softValues()
.build(key -> createExpensiveGraph(key));
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.maximumSize(10_000)
.refreshAfterWrite(1, TimeUnit.MINUTES)
.build(key -> createExpensiveGraph(key));
Päivitysstrategiaa voidaan käyttää vain LoadingCachessä Toisin kuin häätössä, jos välimuistielementtiä kysytään päivityksen aikana, sen vanha arvo palautetaan edelleen, eikä päivitettyä uutta arvoa palauteta ennen kuin elementin päivitys on valmis.
Cache<Key, Graph> graphs = Caffeine.newBuilder()
.maximumSize(10_000)
.recordStats()
.build();
Tiedonkeruu voidaan ottaa käyttöön Caffeine.recordStats()-menetelmällä. Cache.stats()-menetelmä palauttaa CacheStats-objektin, joka sisältää joitain tilastollisia indikaattoreita, kuten:
hitRate(): Kyselyn välimuistin osumaprosentti
evictionCount(): Häädettyjen välimuistien määrä
mediumLoadPenalty(): Keskimääräinen aika, joka kuluu uusien arvojen lataamiseen
SpringBootin tarjoaman RESTful Controllerin avulla voit helposti kysyä välimuistin käyttöä.