キャッシュについては皆さんよくご存知かと思いますが、プロジェクトにおいてはキャッシュが欠かせません。市場には、Redis、Guava Cache、EHcache などの多くのキャッシュ ツールがあります。
これらのツールについては誰もがよく知っているはずなので、今日は説明しません。ローカル キャッシュの実装方法について話しましょう。上記のツールを参照して、より優れたローカル キャッシュを実現するには、次の 3 つの側面から始める必要があると Pingtou 兄弟は考えています。
1. ストレージ コレクションの選択
ローカル キャッシュを実装するには、ストレージ コンテナーがキー/値の形式のデータ構造である必要があります。Java では、よく使われるマップが集まります。 Map には HashMap、Hashtable、ConcurrentHashMap から選択できます。高い同時実行性でのデータ セキュリティの問題を考慮しない場合は、HashMap を選択できます。高い同時実行性でのデータ セキュリティの問題を考慮する場合は、Hashtable と ConcurrentHashMap のいずれかを選択できます。 ConcurrentHashMap.Collection ですが、ConcurrentHashMap のパフォーマンスが Hashtable よりも優れているため、ConcurrentHashMap を優先します。
2. 期限切れキャッシュの処理
キャッシュはメモリに直接保存されるため、期限切れキャッシュを処理しないと大量のキャッシュがメモリを占有してしまいます。無効なキャッシュの数。これは必要なものではありません。はい。そのため、これらの無効なキャッシュをクリーンアップする必要があります。期限切れキャッシュの処理は、Redis の戦略を参照して実装できます。Redis では、定期的な削除と遅延削除の戦略が採用されています。
定期的な削除戦略
定期的な削除戦略は、期限切れのキャッシュを定期的に検出して削除することです。この戦略の利点は、期限切れのキャッシュが確実に削除されることです。欠点もあります。期限切れのキャッシュは時間内に削除されない可能性があります。これは、設定したタイミング頻度に関係します。もう 1 つの欠点は、キャッシュされたデータが大量にある場合、検出のたびにカップに多大な負荷がかかることです。 。
遅延削除戦略
遅延削除戦略では、キャッシュを使用するときに、まずキャッシュの有効期限が切れているかどうかを判断し、期限切れの場合は削除して空を返します。この戦略の利点は、検索時に有効期限が切れているかどうかのみを判断できるため、CUP への影響が少ないことです。同時に、この戦略には致命的な欠点があります。大量のキャッシュが保存されると、これらのキャッシュは使用されずに期限切れになり、無効なキャッシュになります。これらの無効なキャッシュは、大量のメモリ領域を占有します。そして最終的にはサーバーのメモリがオーバーフローしてしまいます。
Redis の 2 つの有効期限キャッシュ処理戦略について簡単に説明しましたが、それぞれの戦略には独自の長所と短所があります。したがって、使用中に 2 つの戦略を組み合わせることができ、その組み合わせた効果は依然として非常に理想的です。
3. キャッシュの削除戦略
キャッシュの削除は、期限切れのキャッシュ処理とは区別する必要があります。キャッシュの削除とは、キャッシュの数が指定したキャッシュの数に達した場合を意味します。結局のところ、私たちの記憶は無限ではありません。引き続きキャッシュを追加する必要がある場合は、特定の戦略に従って既存のキャッシュの一部を削除して、新しく追加されたキャッシュのためのスペースを確保する必要があります。一般的に使用されるいくつかのキャッシュ削除戦略について学びましょう。
先入れ先出しポリシー
キャッシュ領域が不足して新しいデータを受け入れるための新しい領域を確保できない場合、最初にキャッシュに入ったデータが最初にクリアされます。 。 データ。この戦略は主に、キャッシュされた要素の作成時間を比較します。比較的高いデータ有効性が必要な一部のシナリオでは、最新のデータを確実に利用できるようにすることを優先するために、このタイプの戦略を検討できます。
最も使用されない戦略
有効期限が切れているかどうかに関係なく、要素の使用回数に基づいて、使用頻度が低い要素をクリアします。スペースを空けるために。主に要素の hitCount (ヒット数) を比較する戦略であり、高頻度データの正当性が保証されるシナリオで選択できます。
最近最も使用されていない戦略
有効期限が切れているかどうかに関係なく、要素の最後に使用されたタイムスタンプに基づいて、最も遠い使用されたタイムスタンプを持つ要素をクリアします。スペースを解放します。この戦略は主に、get によってキャッシュが最後に使用された時間を比較します。これはホット データのシナリオにより適しており、ホット データの有効性を確保することが優先されます。
ランダム削除戦略
キャッシュの有効期限が切れているかどうかに関係なく、キャッシュをランダムに削除します。キャッシュされたデータに対する要件がない場合は、この戦略の使用を検討できます。
非削除戦略
キャッシュが指定された値に達すると、キャッシュは削除されませんが、新しいキャッシュは追加できません。これ以上キャッシュを追加することはできませんキャッシュが削除されるまで。
上記は、ローカル キャッシュを実装するために考慮する必要がある 3 つのポイントです。これを読めば、ローカル キャッシュの実装方法がわかります。一緒にローカル キャッシュを実装しましょう。
ローカル キャッシュの実装
このデモでは、ストレージ コレクションとして ConcurrentHashMap を使用するため、同時実行性が高い状況でもキャッシュの安全性を確保できます。期限切れキャッシュの処理については、ここではスケジュールされた削除戦略のみを使用し、スケジュールされた削除と遅延削除戦略は使用しませんでした。自分で試して、期限切れキャッシュの処理にこれら 2 つの戦略を使用できます。キャッシュの削除に関しては、ここでは最小使用戦略を採用します。さて、技術的な選択がわかったので、コードの実装を見てみましょう。
キャッシュ オブジェクト クラス
public class Cache implements Comparable<Cache>{ // 键 private Object key; // 缓存值 private Object value; // 最后一次访问时间 private long accessTime; // 创建时间 private long writeTime; // 存活时间 private long expireTime; // 命中次数 private Integer hitCount; ...getter/setter()...
キャッシュの追加
/** * 添加缓存 * * @param key * @param value */ public void put(K key, V value,long expire) { checkNotNull(key); checkNotNull(value); // 当缓存存在时,更新缓存 if (concurrentHashMap.containsKey(key)){ Cache cache = concurrentHashMap.get(key); cache.setHitCount(cache.getHitCount()+1); cache.setWriteTime(System.currentTimeMillis()); cache.setAccessTime(System.currentTimeMillis()); cache.setExpireTime(expire); cache.setValue(value); return; } // 已经达到最大缓存 if (isFull()) { Object kickedKey = getKickedKey(); if (kickedKey !=null){ // 移除最少使用的缓存 concurrentHashMap.remove(kickedKey); }else { return; } } Cache cache = new Cache(); cache.setKey(key); cache.setValue(value); cache.setWriteTime(System.currentTimeMillis()); cache.setAccessTime(System.currentTimeMillis()); cache.setHitCount(1); cache.setExpireTime(expire); concurrentHashMap.put(key, cache); }
キャッシュの取得
/** * 获取缓存 * * @param key * @return */ public Object get(K key) { checkNotNull(key); if (concurrentHashMap.isEmpty()) return null; if (!concurrentHashMap.containsKey(key)) return null; Cache cache = concurrentHashMap.get(key); if (cache == null) return null; cache.setHitCount(cache.getHitCount()+1); cache.setAccessTime(System.currentTimeMillis()); return cache.getValue(); }
最も使用されていないキャッシュを取得する
/** * 获取最少使用的缓存 * @return */ private Object getKickedKey() { Cache min = Collections.min(concurrentHashMap.values()); return min.getKey(); }
#期限切れキャッシュの検出方法
/** * 处理过期缓存 */ class TimeoutTimerThread implements Runnable { public void run() { while (true) { try { TimeUnit.SECONDS.sleep(60); expireCache(); } catch (Exception e) { e.printStackTrace(); } } } /** * 创建多久后,缓存失效 * * @throws Exception */ private void expireCache() throws Exception { System.out.println("检测缓存是否过期缓存"); for (Object key : concurrentHashMap.keySet()) { Cache cache = concurrentHashMap.get(key); long timoutTime = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - cache.getWriteTime()); if (cache.getExpireTime() > timoutTime) { continue; } System.out.println(" 清除过期缓存 : " + key); //清除过期缓存 concurrentHashMap.remove(key); } } }
以上がJava ローカル キャッシュを実装するには、次の点から始めてください。の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。