【Caffeine进阶】Redis+Caffeine 两级缓存实战,性能爆缸

高性能缓存Caffeine的基本使用方式 Caffeine是基于JDK1.8版本的高性能本地缓存库,它是Guava的增强版,与ConcurrentLinkedHashMap相似,支持并发,并且可以在O(1)的时间复杂度内查找、写入元素。 阅读详情
AI摘要

本文介绍使用Caffeine和Redis搭建两级缓存的两种方式:一种基于Spring Cache注解,另一种通过自定义注解和切面实现。旨在降低业务代码的侵入性,提高缓存管理效率。

在这里插入图片描述

往期回顾

博主前面发过一篇【缓存框架Caffeine】初级篇,主要介绍了Caffeine的入门级使用!地址https://blog.youkuaiyun.com/Number_oneEngineer/article/details/123328014?spm=1001.2014.3001.5501

本文主要介绍

  1. Caffeine以注解的方式来操作缓存,将原本全手动操作方式,改进为本地缓存交给spring管理
  2. Caffeine基于切面操作,自定义注解的方式来操作缓存
  3. 前面两种方式搭配Redis实现两级缓存实战


前言

在高性能的服务架构设计中,缓存是一个不可或缺的环节。在实际的项目中,我们通常会将一些热点数据存储到Redis或MemCache这类缓存中间件中,只有当缓存的访问没有命中时再查询数据库。在提升访问速度的同时,也能降低数据库的压力。

随着不断的发展,这一架构也产生了改进,在一些场景下可能单纯使用Redis类的远程缓存已经不够了,还需要进一步配合本地缓存使用,例如Guava cache或Caffeine,从而再次提升程序的响应速度与服务性能。于是,就产生了使用本地缓存作为一级缓存,再加上远程缓存作为二级缓存的两级缓存架构。

在先不考虑并发等复杂问题的情况下,两级缓存的访问流程可以用下面这张图来表示:

在这里插入图片描述


优点与问题

那么,使用两级缓存相比单纯使用远程缓存,具有什么优势呢?

  • 本地缓存基于本地环境的内存,访问速度非常快,对于一些变更频率低、实时性要求低的数据,可以放在本地缓存中,提升访问速度
  • 使用本地缓存能够减少和Redis类的远程缓存间的数据交互,减少网络I/O开销,降低这一过程中在网络通信上的耗时

但是在设计中,还是要考虑一些问题的,例如数据一致性问题。首先,两级缓存与数据库的数据要保持一致,一旦数据发生了修改,在修改数据库的同时,本地缓存、远程缓存应该同步更新。

另外,如果是分布式环境下,一级缓存之间也会存在一致性问题,当一个节点下的本地缓存修改后,需要通知其他节点也刷新本地缓存中的数据,否则会出现读取到过期数据的情况,这一问题可以通过类似于Redis中的发布/订阅功能解决。

此外,缓存的过期时间、过期策略以及多线程访问的问题也都需要考虑进去,不过我们今天暂时先不考虑这些问题,先看一下如何简单高效的在代码中实现两级缓存的管理。


准备工作

在简单梳理了一下要面对的问题后,下面开始两级缓存的代码实战,我们整合号称最强本地缓存的Caffeine作为一级缓存、性能之王的Redis作为二级缓存。首先建一个springboot项目,引入缓存要用到的相关的依赖:

<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
    <version>2.9.2</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    <version>2.8.1</version>
</dependency>

在application.yml中配置Redis的连接信息:

spring:
  redis:
    host: 127.0.0.1
    port: 6379
    database: 0
    timeout: 10000ms
    lettuce:
      pool:
        max-active: 8
        max-wait: -1ms
        max-idle: 8
        min-idle: 0

在下面的例子中,我们将使用RedisTemplate来对redis进行读写操作,RedisTemplate使用前需要配置一下ConnectionFactory和序列化方式。

/**
 * Description: Redis缓存配置类
 *
 * @author ZhuZiKai
 * @date 2022/3/31 0014
 */
@Configuration
@EnableCaching
@EnableAspectJAutoProxy // 开启AOP自动代理
public class RedisConfig extends CachingConfigurerSupport {
    @Bean
    public FastJsonRedisSerializer fastJson2JsonRedisSerializer() {
        return new FastJsonRedisSerializer(Object.class);
    }

    @Bean
    public StringRedisSerializer StringRedisSerializer() {
        return new StringRedisSerializer();
    }

    @Bean("redis")
    @Primary
    public RedisTemplate initRedisTemplate(RedisConnectionFactory redisConnectionFactory,
                                           StringRedisSerializer stringRedisSerializer,
                                           FastJsonRedisSerializer fastJson2JsonRedisSerializer) throws Exception {
        RedisTemplate redisTemplate = new RedisTemplate();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        redisTemplate.setKeySerializer(stringRedisSerializer);
        redisTemplate.setValueSerializer(stringRedisSerializer);
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        redisTemplate.setHashValueSerializer(fastJson2JsonRedisSerializer);
        redisTemplate.setDefaultSerializer(stringRedisSerializer);

        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

下面我们在单机环境下,将按照对业务侵入性的不同程度,分两个版本来实现两级缓存的使用。


V1.0版本

在spring项目中,提供了CacheManager接口和一些注解,允许让我们通过注解的方式来操作缓存。先来看一下常用几个注解说明:

  • @Cacheable:根据键从缓存中取值,如果缓存存在,那么获取缓存成功之后,直接返回这个缓存的结果。如果缓存不存在,那么执行方法,并将结果放入缓存中。
  • @CachePut:不管之前的键对应的缓存是否存在,都执行方法,并将结果强制放入缓存
  • @CacheEvict:执行完方法后,会移除掉缓存中的数据。

如果要使用上面这几个注解管理缓存的话,我们就不需要配置V1版本中的那个类型为Cache的Bean了,而是需要配置spring中的CacheManager的相关参数,具体参数的配置和之前一样:

@Configuration
public class CacheManagerConfig {
    @Bean
    public CacheManager cacheManager(){
        CaffeineCacheManager cacheManager=new CaffeineCacheManager();
        cacheManager.setCaffeine(Caffeine.newBuilder()
                .initialCapacity(128)
                .maximumSize(1024)
                .expireAfterWrite(60, TimeUnit.SECONDS));
        return cacheManager;
    }
}

然后在启动类上再添加上@EnableCaching注解,就可以在项目中基于注解来使用Caffeine的缓存支持了。下面,再次对Service层代码进行改造。

首先,还是改造查询方法,在方法上添加@Cacheable注解:


@Cacheable(value = "order",key = "#id")
//@Cacheable(cacheNames = "order",key = "#p0")
public Order getOrderById(Long id) {
    String key= CacheConstant.ORDER + id;
    
    //先查询 Redis
    Object obj = redisTemplate.opsForValue().get(key);
    if (Objects.nonNull(obj)){
        log.info("get data from redis");
        return (Order) obj;
    }
    
    // Redis没有则查询 DB
    log.info("get data from database");
    Order myOrder = orderMapper.selectOne(new LambdaQueryWrapper<Order>()
            .eq(Order::getId, id));
            
    redisTemplate.opsForValue().set(key,myOrder,120, TimeUnit.SECONDS);
    return myOrder;
}

@Cacheable注解的属性多达9个,好在我们日常使用时只需要配置两个常用的就可以了。其中value和cacheNames互为别名关系,表示当前方法的结果会被缓存在哪个Cache上,应用中通过cacheName来对Cache进行隔离,每个cacheName对应一个Cache实现。value和cacheNames可以是一个数组,绑定多个Cache。

而另一个重要属性key,用来指定缓存方法的返回结果时对应的key,这个属性支持使用SpringEL表达式。通常情况下,我们可以使用下面几种方式作为key:

#参数名
#参数对象.属性名
#p参数对应下标

在上面的代码中,我们看到添加了@Cacheable注解后,在代码中只需要保留原有的业务处理逻辑和操作Redis部分的代码即可,Caffeine部分的缓存就交给spring处理了。

下面,我们再来改造一下更新方法,同样,使用@CachePut注解后移除掉手动更新Cache的操作:


@CachePut(cacheNames = "order",key = "#order.id")
public Order updateOrder(Order order) {
    log.info("update order data");
    orderMapper.updateById(order);
    //修改 Redis
    redisTemplate.opsForValue().set(CacheConstant.ORDER + order.getId(),
            order, 120, TimeUnit.SECONDS);
    return order;
}

【注意】这里的需要定义方法返回值类型,否则会缓存一个空对象到缓存中对应的key上。当下次执行查询操作时,会直接返回空对象给调用方,而不会执行方法中查询数据库或Redis的操作。

最后,删除方法的改造就很简单了,使用@CacheEvict注解,方法中只需要删除Redis中的缓存即可:


@CacheEvict(cacheNames = "order",key = "#id")
public void deleteOrder(Long id) {
    log.info("delete order");
    orderMapper.deleteById(id);
    redisTemplate.delete(CacheConstant.ORDER + id);
}

可以看到,借助spring中的CacheManager和Cache相关的注解,可以把全手动操作两级缓存的强入侵代码方式,改进为本地缓存交给spring管理,Redis缓存手动修改的半入侵方式。那么,还能进一步改造,使之成为对业务代码完全无入侵的方式吗?


V2.0版本

模仿spring通过注解管理缓存的方式,我们也可以选择自定义注解,然后在切面中处理缓存,从而将对业务代码的入侵降到最低。

首先定义一个注解,用于添加在需要操作缓存的方法上:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DoubleCache {
    String cacheName();
    String key(); //支持springEl表达式
    long l2TimeOut() default 120;
    CacheType type() default CacheType.FULL;
}

我们使用cacheName + key作为缓存的真正key(仅存在一个Cache中,不做CacheName隔离),l2TimeOut为可以设置的二级缓存Redis的过期时间,type是一个枚举类型的变量,表示操作缓存的类型,枚举类型定义如下:

public enum CacheType {
    FULL,   //存取
    PUT,    //只存
    DELETE  //删除
}

因为要使key支持springEl表达式,所以需要写一个方法,使用表达式解析器解析参数:

public static String parse(String elString, TreeMap<String,Object> map){
    elString=String.format("#{%s}",elString);
    //创建表达式解析器
    ExpressionParser parser = new SpelExpressionParser();
    //通过evaluationContext.setVariable可以在上下文中设定变量。
    EvaluationContext context = new StandardEvaluationContext();
    map.entrySet().forEach(entry->
        context.setVariable(entry.getKey(),entry.getValue())
    );

    //解析表达式
    Expression expression = parser.parseExpression(elString, new TemplateParserContext());
    //使用Expression.getValue()获取表达式的值,这里传入了Evaluation上下文
    String value = expression.getValue(context, String.class);
    return value;
}

参数中的elString对应的就是注解中key的值,map是将原方法的参数封装后的结果。简单进行一下测试:

public void test() {
    String elString="#order.money";
    String elString2="#user";
    String elString3="#p0";   

    TreeMap<String,Object> map=new TreeMap<>();
    Order order = new Order();
    order.setId(111L);
    order.setMoney(123D);
    map.put("order",order);
    map.put("user","Hydra");

    String val = parse(elString, map);
    String val2 = parse(elString2, map);
    String val3 = parse(elString3, map);

    System.out.println(val);
    System.out.println(val2);
    System.out.println(val3);
}

执行结果如下,可以看到支持按照参数名称、参数对象的属性名称读取,但是不支持按照参数下标读取,暂时留个小坑以后再处理。

123.0
Hydra
null

先是准备Cache相关参数的配置.

@Configuration
public class CaffeineConfig {
    @Bean
    public Cache<String,Object> caffeineCache(){
        return Caffeine.newBuilder()
                .initialCapacity(128)//初始大小
                .maximumSize(1024)//最大数量
                .expireAfterWrite(60, TimeUnit.SECONDS)//过期时间
                .build();
    }
}

准备工作做完了,下面我们定义切面,在切面中操作Cache来读写Caffeine的缓存,操作RedisTemplate读写Redis缓存。

@Slf4j 
@Component 
@Aspect 
@AllArgsConstructor
public class CacheAspect {
    private final Cache cache;
    private final RedisTemplate redisTemplate;

    @Pointcut("@annotation(com.cn.dc.annotation.DoubleCache)")
    public void cacheAspect() {
    }

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

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

        DoubleCache annotation = method.getAnnotation(DoubleCache.class);
        String elResult = ElParser.parse(annotation.key(), treeMap);
        String realKey = annotation.cacheName() + CacheConstant.COLON + elResult;

        //强制更新
        if (annotation.type()== CacheType.PUT){
            Object object = point.proceed();
            redisTemplate.opsForValue().set(realKey, object,annotation.l2TimeOut(), TimeUnit.SECONDS);
            cache.put(realKey, object);
            return object;
        }
        //删除
        else if (annotation.type()== CacheType.DELETE){
            redisTemplate.delete(realKey);
            cache.invalidate(realKey);
            return point.proceed();
        }

        //读写,查询Caffeine
        Object caffeineCache = cache.getIfPresent(realKey);
        if (Objects.nonNull(caffeineCache)) {
            log.info("get data from caffeine");
            return caffeineCache;
        }

        //查询Redis
        Object redisCache = redisTemplate.opsForValue().get(realKey);
        if (Objects.nonNull(redisCache)) {
            log.info("get data from redis");
            cache.put(realKey, redisCache);
            return redisCache;
        }

        log.info("get data from database");
        Object object = point.proceed();
        if (Objects.nonNull(object)){
            //写入Redis
            redisTemplate.opsForValue().set(realKey, object,annotation.l2TimeOut(), TimeUnit.SECONDS);
            //写入Caffeine
            cache.put(realKey, object);        
        }
        return object;
    }
}

切面中主要做了下面几件工作:

  • 通过方法的参数,解析注解中key的springEl表达式,组装真正缓存的key
  • 根据操作缓存的类型,分别处理存取、只存、删除缓存操作
  • 删除和强制更新缓存的操作,都需要执行原方法,并进行相应的缓存删除或更新操作
  • 存取操作前,先检查缓存中是否有数据,如果有则直接返回,没有则执行原方法,并将结果存入缓存

修改Service层代码,代码中只保留原有业务代码,再添加上我们自定义的注解就可以了:


@DoubleCache(cacheName = "order", key = "#id",
        type = CacheType.FULL)
public Order getOrderById(Long id) {
    Order myOrder = orderMapper.selectOne(new LambdaQueryWrapper<Order>()
            .eq(Order::getId, id));
    return myOrder;
}

@DoubleCache(cacheName = "order",key = "#order.id",
        type = CacheType.PUT)
public Order updateOrder(Order order) {
    orderMapper.updateById(order);
    return order;
}

@DoubleCache(cacheName = "order",key = "#id",
        type = CacheType.DELETE)
public void deleteOrder(Long id) {
    orderMapper.deleteById(id);
}

到这里,基于切面操作缓存的改造就完成了,Service的代码也瞬间清爽了很多,让我们可以继续专注于业务逻辑处理,而不用费心去操作两级缓存了。


总结

本文按照对业务入侵的递减程度,依次介绍了两种种管理两级缓存的方法。至于在项目中是否需要使用二级缓存,需要考虑自身业务情况,如果Redis这种远程缓存已经能够满足你的业务需求,那么就没有必要再使用本地缓存了。毕竟实际使用起来远没有那么简单,本文中只是介绍了最基础的使用,实际中的并发问题、事务的回滚问题都需要考虑,还需要思考什么数据适合放在一级缓存、什么数据适合放在二级缓存等等的其他问题。

实现多级缓存(Redis+Caffeine) 在高性能的服务架构设计中,缓存是一个不可或缺的环节。在实际的项目中,我们通常会将一些热点数据存储到Redis或MemCache这类缓存中间件中,只有当缓存的访问没有命中时再查询数据库。在提升访问速度的同时,也能降低数据库的压力。随着不断的发展,这一架构也产生了改进,在一些场景下可能单纯使用Redis类的远程缓存已经不够了,还需要进一步配合本地缓存使用,例如Guava cache或Caffeine,从而再次提升程序的响应速度与服务性能。 阅读详情

相关推荐

本地高性能缓存Caffeine

本地高性能缓存Caffeine

xiaodigua1024的博客 2566

本地缓存Caffeine

Caffine 是一款高性能的近似LFU(最近最少频率使用)准入策略的本地缓存组件,Caffeine的底层数据存储采用ConcurrentHashMap,使用 Window TinyLfu 回收策略,提供了一个近乎最佳的命中率。适用范围:变更频率低、实时性要求低的数据之前的 removalListener 是异步监听,此处的 writer 方法可以设置同步监听器,同步监听器一个实现了接口 CacheWriter 的实例化对象,我们需要自定义接口的实现类。

m0_57801749的博客 1万+

Redis + Caffeine 太强了!两级缓存架构如何让系统性能飙升90%?

Redis+Caffeine两级缓存架构:高并发系统的性能加速器 在高并发场景下,单缓存架构面临性能瓶颈。Redis+Caffeine组合通过分层缓存设计实现性能突破:Caffeine作为本地一级缓存(纳秒级响应)处理90%以上热数据请求,Redis作为分布式二级缓存承载次热数据,数据库仅处理0.1%的穿透请求。该架构通过三种同步策略(失效模式/异步更新/订阅发布)保障数据一致性,配合LRU+TTL淘汰策略优化内存使用。电商案例显示,该方案使平均响应时间从8ms降至1.2ms,数据库QPS降低92.5%,缓

srlsong的博客 960

本地缓存之王-Caffeine

引言 随着业务体量的增长,使用的缓存方案一般会经过:1)无缓存直接查DB;2)数据同步+Redis;3)多级缓存 三个阶段。 第1阶段直接查DB只能用于小流量场景,随着QPS升高,需要引入缓存来减轻DB压力。 第2阶段一般会通过消息队列将数据同步到Redis,并在数据发生变更时同步更新Redis,业务查询时直接查Redis,如果Redis无数据再去查DB。该方案的缺点是如果Redis发生故障,缓存雪崩会直接将流量打到DB,可能会进一步将DB打挂,导致业务事故。 第3阶段将Redis缓存作为二级缓存,在其上再

诗酒年华 2万+

别再只盯着GPS了!手把手教你用Python仿真UWB定位,30厘米精度是怎么算出来的?

本文详细解析了UWB(超宽带)室内定位技术的原理与实现,通过Python代码演示如何从三边测量法到实现30厘米精度的定位。文章涵盖了UWB的核心数学模型、Python仿真环境搭建、误差分析及优化策略,为开发者提供了实用的技术指南和应用场景。

weixin_26873711的博客 252

别再只盯着轨迹线了!FLUENT DPM后处理实战:从粒子采样到直方图报告的完整流程

本文详细介绍了FLUENT DPM后处理的完整流程,从粒子采样到直方图报告的实战技巧。通过设置采样面、分析.dpm文件数据以及创建加权直方图,帮助工程师获取粒子速度、粒径分布等定量信息,提升多相流模拟的深度分析能力。特别适合需要超越轨迹线分析的CFD工程师。

weixin_28730353的博客 277

Caffeine+Redis两级缓存架构

/支持springEl表达式l2TimeOut 为可以设置的二级缓存 Redis 的过期时间CacheType 是一个枚举类型的变量,表示操作缓存的类型FULL, //存取PUT, //只存DELETE //删除//创建表达式解析器//通过evaluationContext.setVariable可以在上下文中设定变量。//解析表达式//使用Expression.getValue()获取表达式的值,这里传入了Evaluation上下文。

qq_22772699的博客 3514

单细胞多组学数据整合分析实战:从原始数据到跨平台合并

本文详细介绍了单细胞多组学数据整合分析的实战流程,从原始数据处理到跨平台合并分析。通过Seurat和Harmony等工具的应用,帮助研究者有效解决批次效应问题,保留真实的生物学信号。文章包含代码示例和常见问题解决方案,特别适合需要进行单细胞合并分析的研究人员参考。

weixin_30752699的博客 89

Redis+Caffeine 实现两级缓存实战

本文介绍Redis+Caffeine实现两级缓存的方式。一种是常规的方式,一种的基于注解的方式。具体的实现可根据自己项目中的业务场景。至于为什么要用Redis+Caffeine的方式,文章也提到了,目前我们Redis集群压力还算挺大的,而且接口对RT的要求也是比较高的。有一点好的就是我们的数据是每天全量推一边,总量也不大,实时性要求也不强。所以就很适合本地缓存的方式。使用本地缓存也要注意设置容量的大小和过期时间,否则容易出现内存溢出。

Y_eatMeat的博客 2640

SECS/GEM协议学习教程

摘要:SECS/GEM是半导体制造设备通信的国际标准,定义了主机系统与设备间的通信规范,包括设备控制、状态监控、数据采集等功能。协议采用分层架构(应用层、消息层、传输层、物理层),支持关键消息类型如S1F13(建立通信)、S2F13(数据采集)等。通信流程涵盖建立连接、数据交换和终止会话,并通过状态变量(SVID)、事件(CEID)等实现实时交互。典型应用包括设备初始化、报警处理及配方管理,需处理数据类型(如U2、A、L)和通信调试问题。

GitHub个人仓库地址:https://github.com/feiyuluoye ; 个人塔罗牌占卜平台:http://118.126.82.12:8080/ ; 欢迎广大Coding友共同交流讨论。 2056

【网络协议】【TSN】精讲TSN工作原理!图解超赞超详细!!!

TSN有一个服务等级(class of service,CoS)的概念。对TSN网络而言,不同优先级的服务对应图3中的PCP码。3位PCP码定义了0(最低)~7(最高)这8个优先级,传输类型分别对应基础、最大努力、卓越努力、严苛应用、延时和抖动小于100 ms的视频、延时和抖动小于10 ms的音频、内部网络控制、网络控制。其会对网络场景进行不同的匹配,是后续调度、配置设计中会考虑到的数据流调度因素。

风云说通信 2629

Redis两级缓存之Caffeine讲解使用

在高性能的服务架构设计中,缓存是一个不可或缺的环节。在实际的项目中,我们通常会将一些热点数据存储到Redis或MemCache这类缓存中间件中,只有当缓存的访问没有命中时再查询数据库。在提升访问速度的同时,也能降低数据库的压力。随着不断的发展,这一架构也产生了改进,在一些场景下可能单纯使用Redis类的远程缓存已经不够了,还需要进一步配合本地缓存使用,例如或Caffeine,从而再次提升程序的响应速度与服务性能。于是,就产生了使用本地缓存作为一级缓存,再加上远程缓存作为二级缓存的两级缓存架构。

上善若泪 1536

pnpm 卸载教程:彻底移除 pnpm 及全局缓存的方法

卸载 pnpm = 全局包 → CLI → 环境变量 → store 缓存只要按这个顺序来,基本不会留下任何坑。如果你后面还打算重新安装 pnpm / 切换 npm、yarn / 多包管理器共存,这些细节反而更重要,环境一旦干净,后续问题会少很多。

代码简单说 Vue、JAVA、PHP、Node.js 熟练运用,接口、架构、性能全搞定。 925

Redis+Caffeine 两级缓存

本文按照对业务入侵的递减程度,依次介绍了三种管理两级缓存的方法。至于在项目中是否需要使用二级缓存,需要考虑自身业务情况,如果Redis这种远程缓存已经能够满足你的业务需求,那么就没有必要再使用本地缓存了。毕竟实际使用起来远没有那么简单,本文中只是介绍了最基础的使用,实际中的并发问题、事务的回滚问题都需要考虑,还需要思考什么数据适合放在一级缓存、什么数据适合放在二级缓存等等的其他问题。

2501_92606851的博客 1051

《中国电信运营商骨干网:历史、现状与未来演进》系列 第一篇:中国骨干网全景图:一级运营商与专用网络的演进

揭秘国家信息高速公路!一文读懂什么是骨干网,它如何支撑我们的数字生活,以及电信、联通、移动、广电等巨头如何构建这张“国家级动脉”。

maqh_csdn的博客 2036

蓝桥杯 stm32 PWM 测量频率

参考 数据手册:比赛 的板子 上面有两个信号发生器,如下图。通过 扭动 可变电阻 (R40, R39), 来实现 PWM 频率的改变。PA15PB4。PA15,PB4分别使用TIM2_CH1TIM3_CH1来对频率进行捕获。

wuyiyu_的博客 2931

Redis + Caffeine 实现高效的两级缓存架构

本文介绍了如何使用Redis和Caffeine构建高性能的两级缓存系统。通过对比纯Redis方案,两级缓存在性能(P99响应时间从8ms降至2ms)和系统稳定性(网络异常时成功率仍保持92%)方面具有显著优势。文章展示了三个版本的演进:版本1直接在Service中实现缓存逻辑;版本2利用Spring Cache注解简化代码;最终版本通过自定义CacheManager实现更灵活的控制。这种架构特别适合电商等高并发场景,能有效应对秒杀等流量洪峰,同时提高系统容错能力。项目源码已在GitHub和Gitee开源。

xzt12138的博客 1061

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

Redis+Caffeine两级缓存,让访问速度纵享丝滑

在高性能的服务架构设计中,缓存是一个不可或缺的环节。在实际的项目中,我们通常会将一些热点数据存储到Redis或MemCache这类缓存中间件中,只有当缓存的访问没有命中时再查询数据库。在提升访问速度的同时,也能降低数据库的压力。随着不断的发展,这一架构也产生了改进,在一些场景下可能单纯使用Redis类的远程缓存已经不够了,还需要进一步配合本地缓存使用,例如Guava cache或Caffeine,从而再次提升程序的响应速度与服务性能。

z1ztai的博客 3629

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

Redis + Caffeine 两级缓存架构:高性能系统的必备组合

本文探讨如何构建高性能的两级缓存架构,结合本地缓存(Caffeine)与远程缓存(Redis),以应对百万级QPS场景。分析了单一缓存的局限性:Redis存在网络延迟,本地缓存难以保证一致性。提出两级缓存方案,通过Caffeine拦截90%+热点请求(纳秒级延迟),Redis作为共享层确保数据一致性(毫秒级延迟)。详细设计了读写流程:读操作优先查询本地缓存,未命中时穿透至Redis和数据库;写操作先更新DB再删缓存,并通过Redis Pub/Sub广播实现多节点本地缓存同步。提供了Spring Boot集成

lzq617789的博客 1065

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

Redis + Caffeine打造超速两级缓存架构

本文介绍了基于Redis+Caffeine两级缓存的高性能接口设计方案。该接口用于查询快递预计送达时间,面临大促期间的高并发挑战。本地缓存Caffeine提供极速查询(毫秒级响应)和减少网络I/O的优势,但存在数据一致性、不支持持久化等缺点。通过配置Redis和Caffeine的Spring Boot集成方案,实现了先查本地缓存、再查Redis、最后查数据库的三级查询流程。文中详细展示了MySQL表结构、Maven依赖配置、Redis和Caffeine的Spring配置类实现,以及核心业务逻辑的Servic

꯭ 瞎꯭扯꯭蛋꯭的博客 953

性能爆表,Redis+Caffeine 两级缓存实战

在高性能的服务架构设计中,缓存是一个不可或缺的环节。在实际的项目中,我们通常会将一些热点数据存储到Redis或MemCache这类缓存中间件中,只有当缓存的访问没有命中时再查询数据库。在提升访问速度的同时,也能降低数据库的压力。随着不断的发展,这一架构也产生了改进,在一些场景下可能单纯使用Redis类的远程缓存已经不够了,还需要进一步配合本地缓存使用,例如Guava cache或Caffeine,从而再次提升程序的响应速度与服务性能。于是,就产生了使用本地缓存作为一级缓存,再加上远程缓存作为二级缓存的两

小米吃辣椒的博客 1362

Redis+Caffeine 两级缓存实战

在高性能的服务架构设计中,缓存是一个不可或缺的环节。在实际的项目中,我们通常会将一些热点数据存储到Redis或MemCache这类缓存中间件中,只有当缓存的访问没有命中时再查询数据库。在提升访问速度的同时,也能降低数据库的压力。 随着不断的发展,这一架构也产生了改进,在一些场景下可能单纯使用Redis类的远程缓存已经不够了,还需要进一步配合本地缓存使用,例如Guava cache或Caffeine,从而再次提升程序的响应速度与服务性能。于是,就产生了使用本地缓存作为一级缓存,再加上远程缓存作为二级缓存的两

azhou的代码园 721

Redis+Caffeine多级缓存架构代码实战

使用 MD5 对查询参数进行 hash 化,确保缓存 key 唯一性;实现了两级缓存(本地缓存 + Redis 分布式缓存),提高数据查询速度并降低数据库压力;设置适当的缓存过期时间和随机化TTL,防止缓存雪崩;对查询参数进行严格校验,既保护系统资源,又确保用户只能获取符合审核要求的数据。这种双缓存策略属于常见的“先查询本地缓存,再查询分布式缓存,最后查询数据库,更新缓存”的应用场景,适用于高并发系统中对响应时间和数据一致性要求较高的场景。

web2u的博客 2017

两级缓存 Caffeine + Redis 架构:原理、实现与实践

本文探讨高性能服务架构中的两级缓存实现方案。通过整合Caffeine(本地缓存)和Redis(远程缓存),提出两种实现方式:1)基于Spring缓存注解的半入侵式方案,利用@Cacheable等注解管理本地缓存,手动操作Redis;2)自定义注解+切面的全解耦方案,通过AOP统一处理缓存逻辑。两种方案均能提升访问速度、减少网络开销,但需重点解决数据一致性问题。实际应用中需根据业务场景权衡,考虑数据特性、一致性成本等因素,避免过度设计。文章提供了完整的代码示例,包括缓存配置、注解定义和切面实现,为开发者实施两

m0_72945550的博客 2002

探究Redis + Caffeine两级缓存架构

本文首先介绍了caffeine的原理,通过手写Redis+Caffeine实现两级缓存进一步理解了缓存的用法,在实际生产项目中,更推荐JetCache实现多级缓存,最后介绍了在一些特殊场景中推荐使用的本地缓存的做法。缓存是一把双刃剑,提升了查询效率。redis的无故障时间虽然很少,但也存在故障的可能性,因此在项目设计过程中,应采用冗余兜底策略,使查询的数据高效准确。

一瓢西湖水的博客 481
上一篇: java中如何查询一个指定的子字符在字符串中出现的所有or指定次数的下标索引
下一篇: 浅谈TCP三次握手,四次挥手
_Romeo
_Romeo 优质创作者: 后端开发技术领域 优质创作者: 后端开发技术领域
博客等级 码龄8年 2万+粉丝 104原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_Romeo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值