キャッシュされたCache Assideモード

7163 ワード

シーケンス


ここでは主にキャッシュされたCache Assideモードについて述べる.

Cache Aside


2つのポイントがあります.
  • アプリケーションはまずcacheからデータを取り出し、得られなかったらデータベースからデータを取り出し、成功したらキャッシュに格納します.
  • 更新はまずデータベースを更新し、成功した後、キャッシュを失効させる.なぜデータベースを書き終わった後にキャッシュを更新しないのですか?主に2つの同時書き込み操作が汚いデータを招くことを恐れている.
  • public V read(K key) {
      V result = cache.getIfPresent(key);
      if (result == null) {
        result = readFromDatabase(key);
        cache.put(key, result);
      }
    
      return result;
    }
    
    public void write(K key, V value) {
      writeToDatabase(key, value);
      cache.invalidate(key);
    };
    

    汚れたデータ


    一つは読み取り操作ですが、キャッシュにヒットしていません.それからデータベースにデータを取りに行きます.この时、書き込み操作が来て、データベースを書き終わったら、キャッシュを失効させます.それから、前の読み取り操作で古いデータを入れます.だから、汚いデータになります.
    このcaseは理論的に現れますが、実際に現れる確率は非常に低いかもしれません.この条件は、リードキャッシュ時にキャッシュが失効し、書き込み操作が同時に発生する必要があるからです.実際には、データベースの書き込み操作は読み取り操作よりもずっと遅く、テーブルをロックしますが、読み取り操作は書き込み操作の前にデータベース操作に入る必要があり、書き込み操作よりも遅くキャッシュを更新する必要があります.これらの条件はほとんどありません.

    maven

            
            
                com.github.ben-manes.caffeine
                caffeine
                2.5.5
            
            
            
                com.google.guava
                guava
                22.0
            
    
    

    コード再現


    ここではコードを使用してこの汚いデータシーンを再現します.
  • 読み取り操作が入ってきて、cacheがないことを発見するとloadingをトリガし、データを取得し、
  • に戻っていない.
  • 書き込み操作入力、データソース更新、invalidateキャッシュ
  • loadingが取得した古いデータは戻り、cacheには汚れたデータ
  • が格納されている.
    @Test
        public void testCacheDirty() throws InterruptedException, ExecutionException {
            AtomicReference db = new AtomicReference<>(1);
    
            LoadingCache cache = CacheBuilder.newBuilder()
                    .build(
                    new CacheLoader() {
                        public Integer load(String key) throws InterruptedException {
                            LOGGER.info("loading reading from db ...");
                            Integer v = db.get();
                            LOGGER.info("loading read from db get:{}",v);
                            Thread.sleep(1000L); // 1 , 
                            LOGGER.info("loading Read from db return : {}",v);
                            return v;
                        }
                    }
            );
    
            Thread t2 = new Thread(() -> {
                try {
                    Thread.sleep(500L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                LOGGER.info("Writing to db ...");
                db.set(2);
                LOGGER.info("Wrote to db");
                cache.invalidate("k");
                LOGGER.info("Invalidated cached");
            });
    
            t2.start();
    
            // t2 invalidate    cache loading
            //loading sleep, invalidate ,cache loading 
            // cache 
            LOGGER.info("fire loading cache");
            LOGGER.info("get from cache: {}",cache.get("k"));
    
            t2.join();
    
            for(int i=0;i<3;i++){
                LOGGER.info("get from cache: {}",cache.get("k"));
            }
        }
    

    しゅつりょく
    15:54:05.751 [main] INFO com.example.demo.CacheTest - fire loading cache
    15:54:05.772 [main] INFO com.example.demo.CacheTest - loading reading from db ...
    15:54:05.772 [main] INFO com.example.demo.CacheTest - loading read from db get:1
    15:54:06.253 [Thread-1] INFO com.example.demo.CacheTest - Writing to db ...
    15:54:06.253 [Thread-1] INFO com.example.demo.CacheTest - Wrote to db
    15:54:06.253 [Thread-1] INFO com.example.demo.CacheTest - Invalidated cached
    15:54:06.778 [main] INFO com.example.demo.CacheTest - loading Read from db return : 1
    15:54:06.782 [main] INFO com.example.demo.CacheTest - get from cache: 1
    15:54:06.782 [main] INFO com.example.demo.CacheTest - get from cache: 1
    15:54:06.782 [main] INFO com.example.demo.CacheTest - get from cache: 1
    15:54:06.782 [main] INFO com.example.demo.CacheTest - get from cache: 1
    

    caffeineの使用

    @Test
        public void testCacheDirty() throws InterruptedException, ExecutionException {
            AtomicReference db = new AtomicReference<>(1);
    
            com.github.benmanes.caffeine.cache.LoadingCache cache = Caffeine.newBuilder()
                    .build(key -> {
                        LOGGER.info("loading reading from db ...");
                        Integer v = db.get();
                        LOGGER.info("loading read from db get:{}",v);
                        Thread.sleep(1000L); // 1 , 
                        LOGGER.info("loading Read from db return : {}",v);
                        return v;
                    });
    
            Thread t2 = new Thread(() -> {
                try {
                    Thread.sleep(500L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                LOGGER.info("Writing to db ...");
                db.set(2);
                LOGGER.info("Wrote to db");
                cache.invalidate("k");
                LOGGER.info("Invalidated cached");
            });
    
            t2.start();
    
            // t2 invalidate    cache loading
            //loading sleep, invalidate ,cache loading 
            // cache 
            LOGGER.info("fire loading cache");
            LOGGER.info("get from cache: {}",cache.get("k"));
    
            t2.join();
    
            for(int i=0;i<3;i++){
                LOGGER.info("get from cache: {}",cache.get("k"));
            }
        }
    

    しゅつりょく
    16:05:10.141 [main] INFO com.example.demo.CacheTest - fire loading cache
    16:05:10.153 [main] INFO com.example.demo.CacheTest - loading reading from db ...
    16:05:10.153 [main] INFO com.example.demo.CacheTest - loading read from db get:1
    16:05:10.634 [Thread-1] INFO com.example.demo.CacheTest - Writing to db ...
    16:05:10.635 [Thread-1] INFO com.example.demo.CacheTest - Wrote to db
    16:05:11.172 [main] INFO com.example.demo.CacheTest - loading Read from db return : 1
    16:05:11.172 [main] INFO com.example.demo.CacheTest - get from cache: 1
    16:05:11.172 [Thread-1] INFO com.example.demo.CacheTest - Invalidated cached
    16:05:11.172 [main] INFO com.example.demo.CacheTest - loading reading from db ...
    16:05:11.172 [main] INFO com.example.demo.CacheTest - loading read from db get:2
    16:05:12.177 [main] INFO com.example.demo.CacheTest - loading Read from db return : 2
    16:05:12.177 [main] INFO com.example.demo.CacheTest - get from cache: 2
    16:05:12.177 [main] INFO com.example.demo.CacheTest - get from cache: 2
    16:05:12.177 [main] INFO com.example.demo.CacheTest - get from cache: 2
    

    ここでinvalidateが見えるとき、loadingは再びトリガーされ、汚れたデータが消去されます.

    doc

  • キャッシュ更新されたソケット
  • caffeine:Java 8高性能キャッシュパッケージ