专业的编程技术博客社区

网站首页 > 博客文章 正文

双层缓存(双缓冲区的基本原理)

baijin 2024-09-11 00:53:18 博客文章 6 ℃ 0 评论

背景

在平时的工作做中,为了提升系统的吞吐量,减少对数据库的访问,我们会使用缓存,优化系统的查询接口,这样可以极大的提升我们的系统吞吐量

我们可以简单地看一下增加了缓存的流程图


对于一般的应用,使用一级缓存已经可以满足我们的要求,但是,对于那些需要支持上万,十万,甚至百万并发的应用,使用一级缓存已经无法满足我们的要求

一级缓存存在的问题

  1. 无法通过扩容应用节点来解决请求量不断增加的问题,不满足云原生的要求
  1. 每次都需要通过网络请求,浪费了很多时间

那么我们应该如何解决一级缓存存在的问题的,接下来,二级缓存应运而生了。

我们来简单地看一下二级缓存的流程图



二级缓存是在应用本地增加了一层内存缓存,这样,既解决了访问缓存的网络请求问题,又解决了通过扩容应用节点承接更多请求的问题。

优点和缺点

一级缓存使用本地缓存节省了网络请求,访问速度更快,并且通过扩容节点就可以承接更多的流量。但是它同时也存在一些其他的问题,例如缓存一致性问题,当我们使用两级缓存的时候,这个问题会变得更加突出,我们不仅要保持这两级缓存的一致,通过还需要保证缓存和数据库的一致性

对于分布式缓存,由于一级缓存是存储在每个应用实例上,因此,还存在一级缓存之间的一致性问题,当一个节点的一级缓存更新后,还需要通知其他节点更新一级缓存。

代码实现

/**
 * 双重缓存切面
 *
 * @author haidechizi
 */
@Aspect
public class DoubleCacheAspect {

    @Pointcut("@annotation(com.haidechizi.doublecache.annotation.DoubleCache)")
    public void cacheAspect() {

    }

    @Around("cacheAspect()")
    public Object doAround(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        DoubleCache doubleCache = method.getAnnotation(DoubleCache.class);

        ProcessService processService = ProcessHolder.getProcessService(doubleCache.type().getCode());
        ProcessContext processContext = new AopProcessContext(point);
        processService.process(processContext);
        return processContext.getValue();
    }
}



/**
 * 基类
 */
public abstract class BaseProcessService implements ProcessService {

    @Autowired
    protected LocalCacheService localCacheService;

    @Autowired
    protected RemoteCacheService remoteCacheService;

    @PostConstruct
    public void init() {
        ProcessHolder.addProcessService(name(), this);
    }

    @Override
    public void process(ProcessContext processContext) throws Throwable {
        cacheKey(processContext);
        doProcess(processContext);
    }

    /**
     * do
     *
     * @param processContext
     */
    protected abstract void doProcess(ProcessContext processContext) throws Throwable;

    protected void cacheKey(ProcessContext processContext) {
        //拼接解析springEl表达式的map
        String[] paramNames = processContext.getParameterNames();
        Object[] args = processContext.getArgs();
        Map<String, Object> treeMap = new TreeMap<>();
        for (int i = 0; i < paramNames.length; i++) {
            treeMap.put(paramNames[i], args[i]);
        }

        DoubleCache doubleCache = processContext.getDoubleCache();
        String paramKey = ElParser.parse(doubleCache.key(), treeMap);
        String cacheKey = doubleCache.cacheName() + CacheConstant.CACHE_SEPARATOR + paramKey;
        processContext.setCacheKey(cacheKey);
    }


    protected abstract String name();
}


public class DeleteProcessService extends BaseProcessService {

    @Override
    protected void doProcess(ProcessContext processContext) throws Throwable {
        String cacheKey = processContext.getCacheKey();
        localCacheService.deleteCache(cacheKey);
        remoteCacheService.deleteCache(cacheKey);
        Object value = processContext.proceed();
        processContext.setValue(value);
    }

    @Override
    protected String name() {
        return CacheType.DELETE.getCode();
    }
}

@Slf4j
public class GetProcessService extends BaseProcessService {

    @Override
    protected void doProcess(ProcessContext processContext) throws Throwable {
        //读写,查询Caffeine
        String cacheKey = processContext.getCacheKey();
        DoubleCache doubleCache = processContext.getDoubleCache();
        Object localCache = localCacheService.getCache(cacheKey);
        if (Objects.nonNull(localCache)) {
            log.info("get data from localCache");
            processContext.setValue(localCache);
            return;
        }

        //查询Redis
        Object removeCache = remoteCacheService.getCache(cacheKey);
        if (Objects.nonNull(removeCache)) {
            log.info("get data from removeCache");
            localCacheService.setCache(cacheKey, removeCache, doubleCache.firstCacheTimeout());
            processContext.setValue(removeCache);
            return;
        }

        log.info("get data from database");
        Object object = processContext.proceed();
        if (Objects.nonNull(object)) {
            localCacheService.setCache(cacheKey, object, doubleCache.firstCacheTimeout());
            remoteCacheService.setCache(cacheKey, object, doubleCache.secondCacheTimeout());
        }
        processContext.setValue(object);
    }

    @Override
    protected String name() {
        return CacheType.GET.getCode();
    }
}


public class PutProcessService extends BaseProcessService {

    @Override
    protected void doProcess(ProcessContext processContext) throws Throwable {
        Object object = processContext.proceed();
        String cacheKey = processContext.getCacheKey();
        DoubleCache doubleCache = processContext.getDoubleCache();
        localCacheService.setCache(cacheKey, object, doubleCache.firstCacheTimeout());
        remoteCacheService.setCache(cacheKey, object, doubleCache.secondCacheTimeout());
        processContext.setValue(object);
    }

    @Override
    protected String name() {
        return CacheType.PUT.getCode();
    }
}

详细代码可以看https://gitee.com/haidechizi/double-cache

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表