SpringBoot及集成配置的零碎知识点(回顾+巩固)

目录

一.多数据源配置

        1.参数配置(properties文件映射)

        2.Mapper映射 

二.Redis

        1.@Cacheable(缓存)

        2.空值问题

        3.缓存的过期时间

        4.过期策略

        5.过期淘汰策略

        6.数据备份和恢复


一.多数据源配置

        1.参数配置(properties文件映射)

                方法①@Value

    @Value("${spring.datasource.driver-class-name}")
    private String driverClassName;
    @Value("${spring.datasource.url}")
    private String url;
    @Value("${spring.datasource.username}")
    private String username;
    @Value("${spring.datasource.password}")
    private String password;

                方法②:@ConfigurationProperties

    @ConfigurationProperties(prefix = "spring.datasource")

                方法③:注入Environment(不常用复用性差,可读性差)

    @Autowired
    private Environment environment;
 
    public String getDatabase() {
        //key为spring.datasource.xxx,一次获取一个值
        return environment.getProperty(key);
    }

                properties: 

spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://192.0.0.0:8000/XXXserverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8
spring.datasource.username=XXX
spring.datasource.password=XXX

                基于MyBatis 操作数据库,实际上就是通过 SqlSession 获取一个 JDBC 连接调用API 

        2.Mapper映射 

                @MapperScan:扫描package把Mapper接口生成实例并注入IOC容器

    @MapperScan(basePackages = {"com.seatrend.pda.admin.dao.firstDao"}, 
    sqlSessionTemplateRef = "sqlSessionTemplate")

                ①basePackages:Mapper文件包的路径

                ②sqlSessionTemplateRef:保证线程安全

二.Redis

        相关注解:

                @Cacheable  主要是针对方法的配置,能够根据方法的求情参数对其结果进行缓存

                @CachePut    保证方法被调用,又希望结果被缓存(缓存更新)

                @CacheEvict  清空缓存(删除数据时的方法)

                @EnableCaching  开启基于注解的缓存

        1.@Cacheable(缓存)

                基于缓存的方法功能,在被调用的时候spring会优先查看是否被被调用过/存在缓存,如果存在则会到缓存中直接取数据,反之则调用接口并存入缓存(配置决定缓存的时间点),在主启动类上需要@EnableCaching注解。

                创建方法:

                方法①:@Cacheable

    @Cacheable(key="'head' + #p0 + #p1 + p2")

                其中p0、p1、p2代表我们接口上的参数

                若接收Json对象(json),则key="#json.getKey('key')"

                方法②:代码创建

    String key = "head" + args0 + args1 + args2;
	boolean hasKey = redisUtil.hasKey(key);
    //如果存在缓存则从缓存中取值
	if (hasKey) {
		Object obj = redisUtil.get(key);
		return obj;
	} else {
    //不存在缓存进行自己的逻辑返回值
		Object result = xxx;
		return result;
	}

        2.空值问题

                空值问题是指,例如账号注册时未注册账号存入缓存后,用户又去注册了,那么注册后再登录会从缓存中取出为空的数据,诸如此类的问题。

                为空的结果集我们不想存入缓存,需要用到Cacheableunless

    @Cacheable(key="'head' + #p0 + #p1 + p2", unless = "#result==null")

                unless的意义是"除了",也就是说除了unless为真的结果都存入缓存(不与sync同时使用)

                使用unless同时也会有别的问题,也就是所有空结果的请求都会对数据库进行交互,数据库的压力会增大,会有压力隐患,引出第三个问题"缓存的过期时间",通过定期的对空值的缓存进行清楚,就能解决空值问题。

        3.缓存的过期时间

                Spring并没有单独为Cacheable提供过期时间配置,我们可以对CacheManager进行配置来设置缓存过期时间。

                进入Redis的配置类

                方式①:

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();  // 生成一个默认配置,通过config对象即可对缓存进行自定义配置
        config = config.entryTtl(Duration.ofMinutes(2))     // 设置缓存的默认过期时间,也是使用Duration设置
                .disableCachingNullValues();     // 不缓存空值
 
        // 设置一个初始化的缓存空间set集合
        Set<String> cacheNames = new HashSet<>();
        cacheNames.add("catalog_test_id");
        cacheNames.add("catalog_test_name");
 
        // 对每个缓存空间应用不同的配置
        Map<String, RedisCacheConfiguration> configMap = new HashMap<>();
        configMap.put("catalog_test_id", config);
        configMap.put("catalog_test_name", config.entryTtl(Duration.ofMinutes(5)));
 
        RedisCacheManager cacheManager = RedisCacheManager.builder(factory)     // 使用自定义的缓存配置初始化一个cacheManager
                .initialCacheNames(cacheNames)  // 注意这两句的调用顺序,一定要先调用该方法设置初始化的缓存名,再初始化相关的配置
                .withInitialCacheConfigurations(configMap)
                .build();
        return cacheManager;
    }
    @CacheConfig(cacheNames = "catalog_test_name")
    public class SsoCache{
	    @Cacheable(keyGenerator = "wiselyKeyGenerator")
	    public String getTokenByGsid(String gsid) 
    }

   /**
    * 自定义Redis Cache 自动化配置替换系统默认的cacheManager
    * 扩展cache name 支持 # 号分隔 cache name 和 超时 ttl(单位秒)
    * 示例:@CachePut(value = "user#300", key = "#id")
    */


    //使用(name中增加“#”,后面是过期时间,不加则走默认时间)
    @Cacheable(cacheNames = "catalog_test_name#120", unless = "#result==null")
    public UserEntity findUserByUserName(String userName) {
        return userRepository.findUserByUserName(userName);
    }
/**
 * redis 配置类
 */
@Slf4j
public class RedisConfigCacheManager extends RedisCacheManager {
 
 
    public RedisConfigCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) {
        super(cacheWriter, defaultCacheConfiguration);
    }
 
    private static final RedisSerializationContext.SerializationPair<Object> DEFAULT_PAIR = RedisSerializationContext.SerializationPair
            .fromSerializer(new GenericJackson2JsonRedisSerializer());
 
    private static final CacheKeyPrefix DEFAULT_CACHE_KEY_PREFIX = cacheName -> cacheName+":";
 
    @Override
    protected RedisCache createRedisCache(String name, RedisCacheConfiguration cacheConfig) {
        final int lastIndexOf = StringUtils.lastIndexOf(name, '#');
        if (lastIndexOf > -1) {
            final String ttl = StringUtils.substring(name, lastIndexOf + 1);
            final Duration duration = Duration.ofSeconds(Long.parseLong(ttl));
            cacheConfig = cacheConfig.entryTtl(duration);
            //修改缓存key和value值的序列化方式
            cacheConfig = cacheConfig.computePrefixWith(DEFAULT_CACHE_KEY_PREFIX)
                    .serializeValuesWith(DEFAULT_PAIR);
            final String cacheName = StringUtils.substring(name, 0, lastIndexOf);
            return super.createRedisCache(cacheName, cacheConfig);
        }else{
            //修改缓存key和value值的序列化方式
            cacheConfig = cacheConfig.computePrefixWith(DEFAULT_CACHE_KEY_PREFIX)
                    .serializeValuesWith(DEFAULT_PAIR);
            return super.createRedisCache(name, cacheConfig);
        }
    }
 
}
@Configuration
public class CacheConfig {

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        ObjectMapper om = new ObjectMapper();
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(
                Object.class);
        // 解决查询缓存转换异常的问题
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        // 配置序列化(解决乱码的问题)
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofMillis(-1))
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
                .disableCachingNullValues();
        RedisCacheWriter cacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);
        return new RedisConfigCacheManager(cacheWriter, config);
    }

}

                方式②:

    @Bean
    @Primary
    public CacheManager cacheManager(RedisTemplate redisTemplate) {
        RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate);
        Map<String, Long> expires = new HashMap<>();
        expires.put("timeout", 60L);
        // 设置超时
        // 根据特定名称设置有效时间
        redisCacheManager.setExpires(expires);
        // 设置默认的时间
        redisCacheManager.setDefaultExpiration(cacheDefaultExpiration);
        return redisCacheManager;
 
    }
    @Configuration
    //@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 3600 * 12)//最大过期时间
    @EnableCaching
    public class RedisConfig {
        @Bean
        public CacheManager cacheManager(RedisTemplate redisTemplate) {
            RedisCacheManager rcm = new RedisCacheManager(redisTemplate);
            //设置缓存过期时间
            Map<String, Long> expires = new HashMap<>();
            expires.put("12h", 3600 * 12L);
            expires.put("1h", 3600 * 1L);
            expires.put("10m", 60 * 10L);
            rcm.setExpires(expires);
            //rcm.setDefaultExpiration(60 * 60 * 12);//默认过期时间
            return rcm;
        }
    }

    @Cacheable(value = "12h", key = "#root.methodName")
    @Override
    public List<User> getUserArticleRank() {
        //获得排行榜前10名的用户,每12小时刷新一次
        return userRepository.findTop10ByArticleSize();
    }

        4.过期策略

                ①:定时过期

                每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除

                优点:该策略可以立即清除过期的数据,对内存很友好

                缺点:若过期key很多,删除这些key会会占用大量的CPU资源去处理过期的数据,从而影响性能

                ②:惰性过期

                只有当访问一个key时,才会判断该key是否已过期,过期则清除。

                优点:该策略可以最大化地节省CPU资源:删除操作只发生在取出key的时候发生,而且只删除当前key,所以对CPU时间的占用是比较少的,而且此时的删除是已经到了非做不可的地步(如果此时还不删除的话,我们就会获取到了已经过期的key了)

                缺点:对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存,甚至导致内存泄漏

                ③:定期过期

                每隔一定的时间,会扫描一定数量的的key,并清除其中已过期的key。

                该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。

                redis的过期删除策略就是:惰性删除和定期删除两种策略配合使用

                惰性删除:在进行get或 setnx等操作时,先检查 key是否过期,若过期,删除key,然后执行键不存在的操作;未过期则不操作,继续执行原有的命令。

                定期删除:遍历每个数据库,(就是redis.conf配置的database 数量,默认为16),从库中随机取出20个键检查是否过期,若没有一个过期键,继续遍历下一个库,若存在键过期,则删除该键。

                定时删除的运行频率:在redis 2.6版本中,规定每秒运行10次,大概100ms运行一次。在Redis2.8版本后,可以通过修改配置文件redis.conf 的 hz 选项来调整这个次数。(建议不要超过100,否则对CPU压力大)
 

        5.过期淘汰策略

                如果Redis占用内存过多的时候,此时会进行内存淘汰,有如下一些策略:

                noeviction:当内存使用超过配置的时候会返回错误,不会驱逐任何键

                allkeys-lru:加入键的时候,如果过限,首先通过LRU算法驱逐最久没有使用的键

                volatile-lru:加入键的时候如果过限,首先从设置了过期时间的键集合中驱逐最久没有使用的键

                allkeys-random:加入键的时候如果过限,从所有key随机删除

                volatile-random:加入键的时候如果过限,从过期键的集合中随机驱逐

                volatile-ttl:从配置了过期时间的键中驱逐马上就要过期的键

                volatile-lfu:从所有配置了过期时间的键中驱逐使用频率最少的键

                allkeys-lfu:从所有键中驱逐使用频率最少的键

        6.数据备份和恢复

                ①:RDB:数据备份之前会检查key是否过期,过期key不会进入RDB文件。数据恢复时,也会对key进行过期检查,过期key不导入数据库。

                ②:AOF:重写时会先判断key是否过期,已过期的key不会重写到AOF文件。

        7.sync(主从同步)

                主要讲Cacheablesync的使用(不与unless同时使用)

                 ①:全量同步

                理解为将需要的数据查询完后,再存入缓存在使用时如果期间服务重启出现假死,那么RedisWriteThroughCallback方法将不会执行unlok(清除缓存key)方法,那么等key失效后key在redis中永久有效就会进入死循环,这个时候可以给这个方法的key写一个定时任务定期删除key

                ②:增量同步

                理解为边查询边存入缓存,对于数据量大查询慢)且访问量大的接口,在使用增量同步时容易造成数据崩溃缓存击穿)。

                

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值