乐趣区

关于缓存:缓存更新策略

近段时间在学习缓存相干常识的时候,看到了缓存更新策略,于是就依据本人的了解,写下这篇文章

分类
  • Cache Aside
  • Read / Write Though
  • Write Behind
Cache Aside
  1. 步骤

    1. 读申请未命中缓存,取数据库数据,并回写缓存
    2. 写申请先更新数据库,再让缓存生效
  2. 长处

    1. 实现简略,调用者可控制数据长久化的细节
  3. 毛病

    1. 下层须要同时治理缓存与长久化,调用较简单
    2. 写申请与读申请并发,读申请持续时间比写申请长,可能会笼罩旧数据到缓存中
  4. 应用场景

    1. 容许缓存数据不精确的场景
    2. 因为并发状况下,可能造成脏数据的状况,所以 QPS 较低场景也能够实用
  5. 代码示例
public class CacheAside<T, K> implements CacheUpdate<T, K>{
    private Map<K, T> map;

    @Override
    public T getData(K key) {
        //if cache has data, return
        return map.get(key);
    }

    @Override
    public boolean updateData(K key, T data) {map.remove(key, data);
        return true;
    }

    @Override
    public boolean addData(K key, T data) {return Objects.nonNull(map.put(key, data));
    }

    @Override
    public boolean removeData(K key) {map.remove(key);
        return true;
    }

    public CacheAside() {map = new HashMap<>();
    }
}
  1. 调用示例
public class CacheAsideClient<T, K> implements CacheUpdateClient<T, K>{public CacheUpdateFactory<T, K> factory = CacheUpdateFactory.getInstance();

    private CacheUpdate<T, K> cacheUpdate;

    private DatabaseOperation<T, K> databaseOperation;

    @Override
    public T getData(K key){
        //get data from cache
        T dataFromCache = cacheUpdate.getData(key);
        //if cache haven't, get from database and put to cache
        if(Objects.nonNull(dataFromCache)){return dataFromCache;}
        T dataFromDatabase = databaseOperation.getData(key);
        cacheUpdate.addData(key, dataFromDatabase);
        return dataFromDatabase;
    }

    @Override
    public boolean updateData(K key, T data){
        //update data to database
        boolean updateToDatabaseRes = databaseOperation.updateData(key, data);
        if(updateToDatabaseRes){
            //invalid cache data
            return cacheUpdate.removeData(key);
        }
        return false;
    }

    @Override
    public boolean addData(K key, T data){
        //add data to database
        return databaseOperation.addData(key, data);
    }

    @Override
    public boolean removeData(K key){
        //remove from database
        boolean removeFromDatabaseRes = databaseOperation.removeData(key);
        if(removeFromDatabaseRes){
            //invalid cache data
            return cacheUpdate.removeData(key);
        }
        return false;
    }

    public CacheAsideClient() {cacheUpdate = factory.getObject(CacheUpdateEnum.CACHE_ASIDE);
        databaseOperation = (DatabaseOperation<T, K>) new MockDatabaseOperation<T>();}
}
Read / Write Though
  1. 步骤

    1. 读 / 写申请都只依赖缓存
    2. 缓存数据同步长久化
  2. 长处

    1. 下层对数据是否长久化 / 长久化实现无感
  3. 毛病

    1. 同步长久化性能较低,但能无效保证数据一致性
  4. 应用场景

    1. 性能要求不高的场景
  5. 代码示例
public class ReadOrWriteThough<T, K> implements CacheUpdate<T, K>{

    private DatabaseOperation<T, K> databaseOperation;

    private Map<K, T> map;

    @Override
    public T getData(K key) {
        //if cache has data, return
        if(map.containsKey(key)){return map.get(key);
        }
        //get data from database and write to cache
        T data = databaseOperation.getData(key);
        map.put(key, data);
        return data;
    }

    @Override
    public boolean updateData(K key, T data) {map.put(key, data);
        return databaseOperation.updateData(key, data);
    }

    @Override
    public boolean addData(K key, T data) {map.put(key, data);
        return databaseOperation.addData(key, data);
    }

    @Override
    public boolean removeData(K key) {map.remove(key);
        return databaseOperation.removeData(key);
    }

    public ReadOrWriteThough() {databaseOperation = (DatabaseOperation<T, K>) new MockDatabaseOperation<>();
        map = new HashMap<>();}
}
  1. 调用示例
public class ReadOrWriteThoughClient<T, K> implements CacheUpdateClient<T, K>{private CacheUpdateFactory<T, K> factory = CacheUpdateFactory.getInstance();

    private CacheUpdate<T, K> cacheUpdate;

    @Override
    public T getData(K key) {return cacheUpdate.getData(key);
    }

    @Override
    public boolean updateData(K key, T data) {return cacheUpdate.updateData(key, data);
    }

    @Override
    public boolean addData(K key, T data) {return cacheUpdate.addData(key, data);
    }

    @Override
    public boolean removeData(K key) {return cacheUpdate.removeData(key);
    }

    public ReadOrWriteThoughClient() {cacheUpdate = factory.getObject(CacheUpdateEnum.READ_WRITE_THOUGH);
    }
}
Write Behind
  1. 步骤

    1. 读 / 写申请都只依赖缓存
    2. 缓存数据异步批量长久化
  2. 长处

    1. 下层对数据是否长久化 / 长久化实现无感
    2. 异步长久化,性能较 Read /Write Though 进步
  3. 毛病

    1. 异步长久化可能会导致数据失落
  4. 应用场景

    1. 性能要求较高的场景
    2. 容许长久化数据失落场景
  5. 代码示例
public class WriteBehind<T, K> implements CacheUpdate<T, K> {

    private Map<K, T> map;

    private DatabaseOperation<T, K> databaseOperation;

    private ThreadPoolExecutor threadPoolExecutor;

    @Override
    public T getData(K key) {if(map.containsKey(key)){return map.get(key);
        }
        T data = databaseOperation.getData(key);
        map.put(key, data);
        return data;
    }

    @Override
    public boolean updateData(K key, T data) {map.put(key, data);
        threadPoolExecutor.execute(() -> databaseOperation.updateData(key, data));
        return true;
    }

    @Override
    public boolean addData(K key, T data) {map.put(key, data);
        threadPoolExecutor.execute(() -> databaseOperation.addData(key, data));
        return true;
    }

    @Override
    public boolean removeData(K key) {map.remove(key);
        threadPoolExecutor.execute(() -> databaseOperation.removeData(key));
        return true;
    }

    public WriteBehind() {map = new HashMap<>();
        databaseOperation = (DatabaseOperation<T, K>) new MockDatabaseOperation<>();
        threadPoolExecutor = new ThreadPoolExecutor(5, 10, 1000, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1000), new ThreadPoolExecutor.CallerRunsPolicy());
    }

}
  1. 调用示例
public class WriteBehindClient<T, K> implements CacheUpdateClient<T, K>{private CacheUpdateFactory<T, K> cacheUpdateFactory = CacheUpdateFactory.getInstance();

    private CacheUpdate<T, K> cacheUpdate;

    @Override
    public T getData(K key) {return cacheUpdate.getData(key);
    }

    @Override
    public boolean updateData(K key, T data) {return cacheUpdate.updateData(key, data);
    }

    @Override
    public boolean addData(K key, T data) {return cacheUpdate.addData(key, data);
    }

    @Override
    public boolean removeData(K key) {return cacheUpdate.removeData(key);
    }

    public WriteBehindClient() {cacheUpdate = cacheUpdateFactory.getObject(CacheUpdateEnum.WRITE_BEHIND);
    }
}
总结
分类 长处 毛病 应用场景
Cache Aside 1. 实现简略,调用者可控制数据长久化的细节 1. 写申请与读申请并发,读申请持续时间比写申请长,可能会笼罩旧数据到缓存中
2. 下层须要同时治理缓存与长久化,调用较简单
1. 容许缓存数据不精确的场景
2. 因为并发状况下,可能造成脏数据的状况,所以 QPS 较低场景也能够实用
Read / Write Though 1. 下层对数据是否长久化 / 长久化实现无感 1. 同步长久化性能较低,但能无效保证数据一致性 1. 性能要求不高的场景
Write Behind 1. 下层对数据是否长久化 / 长久化实现无感
2. 异步长久化,性能较 Read /Write Though 进步
1. 异步长久化可能会导致数据失落 1. 性能要求较高的场景
2. 容许长久化数据失落场景

本文首发于 cartoon 的博客

转载请注明出处:https://cartoonyu.github.io

退出移动版