minhas informações de contato
Correspondência[email protected]
2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
No mundo Java, a tecnologia de cache é amplamente utilizada para melhorar o desempenho do aplicativo e é dividida principalmente em duas categorias: cache remoto e cache local. O cache remoto, com excelente desempenho e flexibilidade, é frequentemente implementado por meio de soluções populares como Redis e Memcached. O cache local, com suas características leves e de acesso rápido, é representado por tecnologias como HashMap, Guava Cache, Caffeine e Ehcache.
Iremos nos aprofundar nos mistérios do cache remoto em postagens futuras do blog, mas hoje vamos nos concentrar no cache local. Este artigo primeiro apresentará a tecnologia de cache local para fornecer uma visão geral abrangente. A seguir, nos aprofundaremos na tecnologia de cache, conhecida como a rainha do desempenho, e exploraremos os princípios e métodos de implementação por trás dela. Finalmente, através de uma série de casos práticos, demonstraremos como utilizar efetivamente essas tecnologias de cache local de alto desempenho no trabalho diário para melhorar a eficiência do desenvolvimento e o desempenho do aplicativo.
Usando a implementação subjacente do Map, podemos armazenar os objetos a serem armazenados em cache diretamente na memória, que é um método direto e eficiente.
Vantagem: Este método é simples e direto, sem depender de bibliotecas externas, e é muito adequado para aplicações com requisitos descomplicados de cache e cenários simples.
Desvantagens: No entanto, este método carece de um mecanismo automático de eliminação de cache. Se estratégias de cache mais avançadas precisarem ser implementadas, poderão ser necessários custos de desenvolvimento personalizados mais elevados.
- 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 é uma tecnologia de cache desenvolvida pelo Google que se baseia no algoritmo de substituição LRU (menos usado recentemente). No entanto, com o aumento da cafeína, o Goiaba Cache desapareceu gradualmente da vista das pessoas. A cafeína não só herda as vantagens do Goiaba Cache, mas também o supera em muitos aspectos. Embora o código de amostra específico não seja fornecido aqui, os leitores interessados no Guava Cache podem visitar seu site oficial para obter mais informações.
Vantagem: O Guava Cache suporta a definição de limites máximos de capacidade e fornece duas estratégias de expiração: com base no tempo de inserção e no tempo de acesso, e também suporta algumas funções estatísticas básicas.
Desvantagens: Com o lançamento do Spring Boot 2 e Spring 5, o Guava Cache não é mais recomendado para ambos.
A cafeína é uma tecnologia de cache de código aberto que usa o algoritmo W-TinyLFU, que é uma estratégia de eliminação de cache que combina as vantagens de LRU e LFU (menor frequência usada). O desempenho do cache do Caffeine está próximo da solução teórica ideal e pode ser considerado como uma versão atualizada do Guava Cache.
- 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 é uma estrutura de cache em processo Java pura, conhecida por seu desempenho rápido e eficiente. É amplamente reconhecido e serve como provedor de cache padrão para o Hibernate.
Vantagem : Ehcache fornece uma ampla gama de algoritmos de remoção de cache, incluindo LFU (menos usado), LRU (menos usado recentemente) e FIFO (primeiro a entrar, primeiro a sair). Ele suporta diferentes tipos de armazenamento de cache, como cache in-heap, cache off-heap e cache de disco, para se adaptar a diferentes necessidades de armazenamento. Além disso, o Ehcache também oferece suporte a uma variedade de soluções de cluster, resolvendo efetivamente o problema de compartilhamento de dados.
Desvantagens: Embora o Ehcache seja excelente em muitos aspectos, ele fica um pouco aquém do Caffeine em termos de desempenho.
-
- 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);
A interface Cache fornece a capacidade de localizar, atualizar e remover elementos armazenados em cache usando pesquisas explícitas. Quando o elemento armazenado em cache não pode ser gerado ou uma exceção é lançada durante o processo de geração e a geração do elemento falha, cache.get pode retornar null .
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);
Um LoadCache é uma implementação de cache de um Cache com o recurso CacheLoader adicionado.
Se o cache não estiver presente, o elemento de cache correspondente será gerado por meio de CacheLoader.load.
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 é a forma assíncrona de Cache, fornecendo ao Executor a capacidade de gerar elementos de cache e retornar CompletableFuture. A implementação padrão do pool de threads é ForkJoinPool.commonPool(), mas você também pode personalizar sua seleção do pool de threads substituindo e implementando o método Caffeine.executor(Executor).
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 é a forma assíncrona de LoadCache, que fornece a função de carregamento assíncrono para gerar elementos de cache.
// 基于缓存内的元素个数进行驱逐
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));
A estratégia de atualização só pode ser usada em LoadCache. Ao contrário do despejo, se o elemento de cache for consultado durante a atualização, seu valor antigo ainda será retornado e o novo valor atualizado não será retornado até que a atualização do elemento seja concluída.
Cache<Key, Graph> graphs = Caffeine.newBuilder()
.maximumSize(10_000)
.recordStats()
.build();
A coleta de dados pode ser ativada usando o método Caffeine.recordStats(). O método Cache.stats() retornará um objeto CacheStats, que conterá alguns indicadores estatísticos, como:
hitRate(): taxa de acerto do cache de consulta
evictionCount(): Número de caches sendo despejados
AverageLoadPenalty(): O tempo médio que leva para um novo valor ser carregado
Com o controlador RESTful fornecido pelo SpringBoot, você pode consultar facilmente o uso do cache.