Javaローカルキャッシュを実現する方法解析


キャッシュ、皆さんはきっとよく知っていると思います。プロジェクトの中でキャッシュは必ず必要です。市場にはRedis、Guava CacheまたはEHcacheのような非常に多くのキャッシュツールがあります。これらのツールについては、皆さんはきっとよく知っていると思います。だから、今日はそれらの話をしないで、どうやってローカルキャッシュを実現するかを話しましょう。上記のいくつかのツールを参考にして、より良いローカルキャッシュを実現します。
1、セットの選択を記憶する
ローカルキャッシュを実現して、格納容器は必ずkey/value形式のデータ構造であり、Javaにおいて、つまり私達がよく使うMap集合である。MapにはHashMap、hashtable、ConccurrenthashMapのいくつかの種類があります。高合併を考慮しないと、データの安全問題があります。HashMapを選択できます。高合併を考慮して、データの安全問題を考慮すれば、Hashtable、ConcerenthashMapの集合を選択できますが、優先的にConcerenthashMapを選択します。ConccurrenthashMapの性能はhashtableより良いからです。
2、キャッシュ失効処理
キャッシュは直接メモリに保存されていますので、もし私たちが古いキャッシュを処理しないと、メモリは大量に無効になります。失効キャッシュ処理はRedisの戦略を参考にして実現できます。Redisは定期的に削除+怠惰淘汰策を採用しています。
ポリシーを定期的に削除
定期的に削除するポリシーは、期間ごとに古いキャッシュを検出し、削除します。このポリシーの利点は、有効期限が切れたキャッシュが削除されることを確認することです。同時に欠点もあります。期限が切れたキャッシュは直ちに削除されるとは限らないです。これは私達が設定したタイミング周波数と関係があります。もう一つの欠点はキャッシュデータが多い場合、毎回検出してもカップに大きな圧力を与えます。
怠惰淘汰策
怠け者淘汰策はキャッシュを使う時にキャッシュが有効期限が切れるかどうかを先に判断して、もし期限が切れたら削除して、そして空に戻ります。この戦略の長所は検索する時だけ有効期限が切れるかどうかを判断します。CUPに影響があります。同時にこのような戦略は致命的な欠点があります。大量のキャッシュを保存した場合、これらのキャッシュは使用されておらず、有効期限が切れました。これらの無効なキャッシュは大量のメモリ空間を占有します。最後にサーバメモリがオーバーフローします。
Redisの2つの期限切れキャッシュ処理戦略を簡単に理解しました。各戦略にはそれぞれの長所と短所があります。したがって、私たちは使用中、二つの戦略を組み合わせることができます。効果はやはり理想的です。
3、キャッシュレス戦略
キャッシュの淘汰と失効キャッシュの処理は区別されています。キャッシュの淘汰とは、私達が指定したキャッシュの個数に達した時、私達のメモリは無限ではありません。私たちは引き続きキャッシュを追加する必要があるなら、既存のキャッシュの中で何らかのポリシーによってキャッシュを淘汰し、新たに追加されたキャッシュに位置を空けて、次のようにいくつかの常用キャッシュの淘汰策を認識する必要があります。
先鋭先出し策
最初にキャッシュに入ったデータはキャッシュスペースが足りない場合、優先的に消去され、新しい空間を作って新しいデータを受け取ることができます。このポリシーは主にキャッシュ要素の作成時間を比較します。データの実効性に対する要求が高い場面では、このようなポリシーの選択が考慮され、最新のデータの利用を優先的に保証する。
少なくともポリシーを使う
有効期限が過ぎていても、元素の使用回数によって、使用回数が少ない元素の放出空間をクリアします。このようなポリシーは、主に要素のhitCount(命中回数)を比較し、高周波データの有効性を保証するシーンで選択できます。
最近は少なくともポリシーを使います。
有効期限が過ぎていても、最後に使用された元素のタイムスタンプによって、タイムスタンプを使用した一番遠い元素の放出空間をクリアします。このポリシーは主にキャッシュの最近の利用時間を比較する。ホットスポットのデータシーンで適用され、ホットスポットデータの有効性を優先的に保証します。
ランダム淘汰策
有効期限が過ぎても、ランダムにキャッシュを淘汰します。キャッシュデータに対して何の要求もないなら、ポリシーを利用してもいいです。
策略をはずさない
キャッシュが指定値に達したら、キャッシュは不要です。キャッシュがあるまでは、キャッシュが追加されません。
上記はローカルキャッシュを実現するために考慮しなければならない三つの点です。見終わったら、どうやってローカルキャッシュを実現するべきかを知るべきです。私たちは一緒にローカルキャッシュを実現してもいいです。
ローカルキャッシュを実行
このDemoでは、保存セットとしてConcerenthashMapを採用しています。このように、高合併の場合でも、キャッシュの安全を保証します。キャッシュの有効期限が切れました。ここではタイミング削除のポリシーを使っています。タイミング削除+怠惰淘汰策を使っていません。自分で試してみてもいいです。キャッシュの淘汰に関しては、私はここで最小限の使用戦略を採用しました。はい、技術の選択は分かりました。コードの実現を見に来ました。
キャッシュオブジェクトクラス

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);
    }
  }
}
上は主要コードです。完全なコードはすでにGitHubにアップロードしました。
本論文では、ローカルキャッシュの設計角度を実現するために、簡単にローカルキャッシュを実現するための注意点を検討したが、これらもキャッシュの核心技術であり、Redis、Guava CacheまたはEHcacheまたは他のキャッシュツールであっても、それらは実現原理上、ローカルキャッシュの実現原理と同じである。ローカルキャッシュの実現原理を理解しさえすれば、これらのキャッシュツールを勉強するほうが楽だと思います。
以上が本文の全部です。皆さんの勉強に役に立つように、私たちを応援してください。