プライベートな連絡先の最初の情報
送料メール:
2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
Java の世界では、キャッシュ テクノロジはアプリケーションのパフォーマンスを向上させるために広く使用されており、主にリモート キャッシュとローカル キャッシュの 2 つのカテゴリに分類されます。優れたパフォーマンスと柔軟性を備えたリモート キャッシュは、多くの場合、Redis や Memcached などの一般的なソリューションを通じて実装されます。軽量で高速なアクセス特性を備えたローカル キャッシュは、HashMap、Guava Cache、Caffeine、Ehcache などのテクノロジによって代表されます。
今後のブログ投稿でリモート キャッシュの謎を詳しく掘り下げていきますが、今日はローカル キャッシュに焦点を当てましょう。この記事では、まずローカル キャッシュ テクノロジについて説明し、包括的な概要を説明します。次に、パフォーマンスの王様として知られるキャッシュ テクノロジを詳しく掘り下げ、その背後にある原理と実装方法を探っていきます。最後に、一連の実践的なケースを通じて、これらの高性能ローカル キャッシュ テクノロジを日常業務で効果的に活用して、開発効率とアプリケーションのパフォーマンスを向上させる方法を示します。
Map の基礎となる実装を使用すると、キャッシュされるオブジェクトをメモリに直接保存できます。これは直接的かつ効率的な方法です。
アドバンテージ: この方法は、外部ライブラリに依存せずにシンプルかつ直接的であり、単純なキャッシュ要件と単純なシナリオを持つアプリケーションに非常に適しています。
短所: ただし、この方法には自動キャッシュ削除メカニズムがありません。より高度なキャッシュ戦略を実装する必要がある場合は、より高いカスタマイズ開発コストが必要になる可能性があります。
- 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 は、LRU (最も最近使用されていない) 置換アルゴリズムに基づいて Google によって開発されたキャッシュ テクノロジです。しかし、カフェインの台頭により、グアバ キャッシュは徐々に人々の目から消えていきました。 カフェインはグアバ キャッシュの利点を継承しているだけでなく、多くの点でそれを上回っています。ここでは具体的なサンプル コードは提供されていませんが、Guava Cache に興味のある読者は公式 Web サイトにアクセスして詳細を確認してください。
アドバンテージ: Guava Cache は、最大容量制限の設定をサポートし、挿入時間とアクセス時間に基づく 2 つの有効期限戦略を提供し、いくつかの基本的な統計関数もサポートします。
短所: Spring Boot 2 と Spring 5 のリリースにより、Guava Cache は両方で推奨されなくなりました。
Caffeine は、LRU と LFU (最小使用頻度) の利点を組み合わせたキャッシュ削除戦略である W-TinyLFU アルゴリズムを使用するオープン ソース キャッシュ テクノロジです。 Caffeine のキャッシュ パフォーマンスは理論上の最適解に近く、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 は、高速で効率的なパフォーマンスで知られる Pure Java のインプロセス キャッシュ フレームワークです。これは広く認識されており、Hibernate のデフォルトのキャッシュ プロバイダーとして機能します。
アドバンテージ : Ehcache は、LFU (最も頻繁に使用されていない)、LRU (最も最近使用されていない)、FIFO (先入れ先出し) など、幅広いキャッシュ削除アルゴリズムを提供します。さまざまなストレージのニーズに適応するために、ヒープ内キャッシュ、オフヒープ キャッシュ、ディスク キャッシュなど、さまざまなタイプのキャッシュ ストレージをサポートしています。さらに、Ehcache はさまざまなクラスター ソリューションもサポートしており、データ共有の問題を効果的に解決します。
短所: Ehcache は多くの点で優れていますが、パフォーマンスの点では Caffeine にわずかに劣ります。
-
- 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);
キャッシュ インターフェイスは、明示的な検索を使用してキャッシュされた要素を検索、更新、削除する機能を提供します。キャッシュされた要素を生成できない場合、または生成プロセス中に例外がスローされて要素の生成に失敗した場合、cache.get は 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);
LoadingCache は、CacheLoader 機能が追加された Cache のキャッシュ実装です。
キャッシュが存在しない場合は、対応するキャッシュ要素が 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 はキャッシュの非同期形式であり、Executor がキャッシュ要素を生成して CompletableFuture を返す機能を提供します。デフォルトのスレッド プール実装は ForkJoinPool.commonPool() ですが、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 は LoadingCache の非同期形式であり、キャッシュ要素を生成するための非同期ロードの機能を提供します。
// 基于缓存内的元素个数进行驱逐
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));
リフレッシュ戦略は、LoadingCache でのみ使用できます。エビクションとは異なり、リフレッシュ中にキャッシュ要素がクエリされた場合でも、その古い値は返されますが、要素のリフレッシュが完了するまで、リフレッシュされた新しい値は返されません。
Cache<Key, Graph> graphs = Caffeine.newBuilder()
.maximumSize(10_000)
.recordStats()
.build();
データ収集は、Caffeine.recordStats() メソッドを使用してオンにできます。 Cache.stats() メソッドは、次のようないくつかの統計指標を含む CacheStats オブジェクトを返します。
hitRate(): クエリキャッシュのヒット率
evictionCount(): 削除されるキャッシュの数
AverageLoadPenalty(): 新しい値がロードされるまでにかかる平均時間
SpringBoot が提供する RESTful コントローラーを使用すると、キャッシュの使用状況を簡単にクエリできます。