cartoony

导航

缓冲更新策略

近段时间在学习缓存相关知识的时候,看到了缓存更新策略,于是就根据自己的理解,写下这篇文章

分类
  • 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

posted on 2022-06-06 22:55  cartoony  阅读(34)  评论(0编辑  收藏  举报