栏目分类:
子分类:
返回
终身学习网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
终身学习网 > IT > 软件开发 > 后端开发 > Java

缓存更新策略

Java 更新时间:发布时间: 百科书网 趣学号

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

分类
  • 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 implements CacheUpdate{
    private Map 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 implements CacheUpdateClient{

    public CacheUpdateFactory factory = CacheUpdateFactory.getInstance();

    private CacheUpdate cacheUpdate;

    private DatabaseOperation 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) new MockDatabaseOperation();
    }
}
Read / Write Though
  1. 步骤

    1. 读/写请求都只依赖缓存
    2. 缓存数据同步持久化
  2. 优点

    1. 上层对数据是否持久化/持久化实现无感
  3. 缺点

    1. 同步持久化性能较低,但能有效保证数据一致性
  4. 使用场景

    1. 性能要求不高的场景
  5. 代码示例

public class ReadOrWriteThough implements CacheUpdate{

    private DatabaseOperation databaseOperation;

    private Map 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) new MockDatabaseOperation<>();
        map = new HashMap<>();
    }
}
  1. 调用示例
public class ReadOrWriteThoughClient implements CacheUpdateClient{

    private CacheUpdateFactory factory = CacheUpdateFactory.getInstance();

    private CacheUpdate 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 implements CacheUpdate {

    private Map map;

    private DatabaseOperation 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) new MockDatabaseOperation<>();
        threadPoolExecutor = new ThreadPoolExecutor(5, 10, 1000, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1000), new ThreadPoolExecutor.CallerRunsPolicy());
    }

}
  1. 调用示例
public class WriteBehindClient implements CacheUpdateClient{

    private CacheUpdateFactory cacheUpdateFactory = CacheUpdateFactory.getInstance();

    private CacheUpdate 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 Aside1. 实现简单,调用者可控制数据持久化的细节1. 写请求与读请求并发,读请求持续时间比写请求长,可能会覆盖旧数据到缓存中
2. 上层需要同时管理缓存与持久化,调用较复杂
1. 允许缓存数据不准确的场景
2. 因为并发情况下,可能造成脏数据的情况,所以 QPS 较低场景也可以适用
Read / Write Though1. 上层对数据是否持久化/持久化实现无感1. 同步持久化性能较低,但能有效保证数据一致性1. 性能要求不高的场景
Write Behind1. 上层对数据是否持久化/持久化实现无感
2. 异步持久化,性能较 Read /Write Though 提高
1. 异步持久化可能会导致数据丢失1. 性能要求较高的场景
2. 允许持久化数据丢失场景

本文首发于cartoon的博客

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

转载请注明:文章转载自 www.051e.com
本文地址:http://www.051e.com/it/957146.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 ©2023-2025 051e.com

ICP备案号:京ICP备12030808号