2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
Dans le monde Java, la technologie de mise en cache est largement utilisée pour améliorer les performances des applications et est principalement divisée en deux catégories : la mise en cache distante et la mise en cache locale. La mise en cache à distance, avec ses excellentes performances et sa flexibilité, est souvent mise en œuvre via des solutions populaires telles que Redis et Memcached. Le cache local, avec ses caractéristiques d'accès léger et rapide, est représenté par des technologies telles que HashMap, Guava Cache, Caffeine et Ehcache.
Nous approfondirons les mystères de la mise en cache à distance dans les prochains articles de blog, mais aujourd'hui, concentrons-nous sur la mise en cache locale. Cet article vous présentera d’abord la technologie de mise en cache locale pour vous fournir un aperçu complet. Nous aborderons ensuite la technologie de mise en cache, connue comme la reine des performances, et explorerons les principes et les méthodes de mise en œuvre qui la sous-tendent. Enfin, à travers une série de cas pratiques, nous démontrerons comment utiliser efficacement ces technologies de mise en cache locale hautes performances dans votre travail quotidien pour améliorer l'efficacité de votre développement et les performances de vos applications.
Grâce à l'implémentation sous-jacente de Map, nous pouvons stocker les objets à mettre en cache directement en mémoire, ce qui est une méthode directe et efficace.
Avantage: Cette méthode est simple et directe, sans recourir à des bibliothèques externes, et convient parfaitement aux applications ayant des exigences de mise en cache simples et des scénarios simples.
Désavantages: Cependant, cette méthode ne dispose pas d'un mécanisme d'élimination automatique du cache. Si des stratégies de mise en cache plus avancées doivent être mises en œuvre, des coûts de développement personnalisés plus élevés peuvent être nécessaires.
- 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 est une technologie de mise en cache développée par Google qui est basée sur l'algorithme de remplacement LRU (le moins récemment utilisé). Cependant, avec l’essor de la caféine, Guava Cache a progressivement disparu de la vue des gens. La caféine hérite non seulement des avantages de Guava Cache, mais la surpasse également à bien des égards. Bien qu'aucun exemple de code spécifique ne soit fourni ici, les lecteurs intéressés par Guava Cache peuvent visiter son site officiel pour plus d'informations.
Avantage: Guava Cache prend en charge la définition de limites de capacité maximales et propose deux stratégies d'expiration : basées sur le temps d'insertion et le temps d'accès, et prend également en charge certaines fonctions statistiques de base.
Désavantages: Avec la sortie de Spring Boot 2 et Spring 5, Guava Cache n'est plus recommandé pour les deux.
Caffeine est une technologie de mise en cache open source qui utilise l'algorithme W-TinyLFU, qui est une stratégie d'élimination du cache qui combine les avantages de LRU et LFU (moindre fréquence utilisée). Les performances du cache de Caffeine sont proches de la solution théorique optimale et peuvent être considérées comme une version améliorée de 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 est un framework de mise en cache in-process purement Java connu pour ses performances rapides et efficaces. Il est largement reconnu et sert de fournisseur de cache par défaut pour Hibernate.
Avantage : Ehcache fournit une large gamme d'algorithmes d'éviction de cache, notamment LFU (le moins fréquemment utilisé), LRU (le moins récemment utilisé) et FIFO (premier entré, premier sorti). Il prend en charge différents types de stockage en cache, tels que le cache dans le tas, le cache hors tas et le cache disque, pour s'adapter aux différents besoins de stockage. De plus, Ehcache prend également en charge une variété de solutions de cluster, résolvant efficacement le problème du partage de données.
Désavantages: Bien qu'Ehcache excelle dans de nombreux aspects, il est légèrement en deçà de Caffeine en termes de performances.
-
- 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);
L'interface Cache offre la possibilité de rechercher, mettre à jour et supprimer des éléments mis en cache à l'aide de recherches explicites. Lorsque l'élément mis en cache ne peut pas être généré ou qu'une exception est levée pendant le processus de génération et que la génération de l'élément échoue, cache.get peut renvoyer 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);
Un LoadingCache est une implémentation de cache d'un Cache avec la fonctionnalité CacheLoader ajoutée.
Si le cache n'est pas présent, l'élément de cache correspondant sera généré via 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 est la forme asynchrone de Cache, offrant à Executor la possibilité de générer des éléments de cache et de renvoyer CompletableFuture. L'implémentation par défaut du pool de threads est ForkJoinPool.commonPool(), mais vous pouvez également personnaliser votre sélection de pool de threads en remplaçant et en implémentant la méthode 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 est la forme asynchrone de LoadingCache, qui fournit la fonction de chargement asynchrone pour générer des éléments 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));
La stratégie d'actualisation ne peut être utilisée que dans LoadingCache. Contrairement à l'expulsion, si l'élément de cache est interrogé lors de l'actualisation, son ancienne valeur sera toujours renvoyée et la nouvelle valeur actualisée ne sera renvoyée qu'une fois l'actualisation de l'élément terminée.
Cache<Key, Graph> graphs = Caffeine.newBuilder()
.maximumSize(10_000)
.recordStats()
.build();
La collecte de données peut être activée à l'aide de la méthode Caffeine.recordStats(). La méthode Cache.stats() renverra un objet CacheStats, qui contiendra certains indicateurs statistiques, tels que :
hitRate() : taux de réussite du cache de requête
evictionCount() : nombre de caches expulsés
AverageLoadPenalty() : Le temps moyen nécessaire au chargement d'une nouvelle valeur
Avec le contrôleur RESTful fourni par SpringBoot, vous pouvez facilement interroger l'utilisation du cache.