多缓存配置

多缓存使用Redis数据库用作缓存存储,可根据自身业务配置多套不同名称的配置交替使用,从配置注入到使用如下。
  1. 第一步在配置文件 application.yaml 中 macula.redis 项下添加自己的Redis配置:
macula:
  ######## 缓存Redis配置 ##############
  redis:
  - name: cache
    singleServerConfig:
      address: ${macula.redis.cache.url}
      password: ${macula.redis.cache.password}
  - name: data
    singleServerConfig:
      address: ${macula.redis.data.url}
      password: ${macula.redis.data.password}
  - name: klock
    singleServerConfig:
      address: ${macula.redis.klock.url}
      password: ${macula.redis.klock.password}
  - name: demo
    singleServerConfig:
      address: ${macula.redis.demo.url}
      password: ${macula.redis.demo.password}

项目启动时, Macula4架构会将 macula.redis 中所有的Redis项配置注册到 bean 实例中, Bean名称为 {redis.name} + “RedissonClient”, 如上demo则实例名称为:demoRedissonClient。具体实现代码可查阅  RedissonClientConfigurationRegistrar.class 。

  1. 第二步 使用demoRedissonClient 创建工厂实例与Redisson序列化
@Configuration
public class DemoRedisConfiguration {
    @Bean(name = "demoRedisConnectionFactory")
    public RedisConnectionFactory demoRedisConnectionFactory(@Qualifier("demoRedissonClient") RedissonClient redissonClient) {
        return new RedissonConnectionFactory(redissonClient);
    }
    @Bean(name = "demoRedisTemplate")
    public RedisTemplate<String, Object> demoRedisTemplate(@Qualifier("demoRedisConnectionFactory") RedisConnectionFactory demoRedisConnectionFactory) {
        // TODO 读取配置好点(Class)kryo.register
        KryoRedisSerializer<Object> kryoRedisSerializer = new KryoRedisSerializer<>(new Class<?> [] { Object.class});
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(demoRedisConnectionFactory);
        // 设置值(value)的序列化采用FastJsonRedisSerializer。
        template.setValueSerializer(kryoRedisSerializer);
        template.setHashValueSerializer(kryoRedisSerializer);
        // 设置键(key)的序列化采用StringRedisSerializer。
        template.setKeySerializer(RedisSerializer.string());
        template.setHashKeySerializer(RedisSerializer.string());
        return template;
    }
    @Bean(name = "demoStringRedisTemplate")
    public StringRedisTemplate demoStringRedisTemplate(@Qualifier("demoRedisConnectionFactory") RedisConnectionFactory demoRedisConnectionFactory) {
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(demoRedisConnectionFactory);
        return template;
    }
}
  1. 第三步 使用
@Resource(name = "demoStringRedisTemplate")
private StringRedisTemplate stringRedisTemplate;

多级缓存

Macula扩展了缓存功能,支持分布式环境的多级缓存框架,一级缓存使用Caffeine作为本地缓存,二级缓存使用redis作为集中式缓存,在配置文件 application.yaml 中 macula.redis 项下使用 - name: cache 为二级缓存数据库 。一级缓存和二级缓存的数据一致性是通过推和拉两种模式相结合的方式来实现的。推主要是基于redis的pub/sub机制,拉主要是基于消息队列和记录消费消息的偏移量来实现的。

支持

  • 支持缓存命中率的监控统计,统计数据上报支持自定义扩展
  • 支持缓存过期时间在注解上直接配置
  • 支持缓存的自动刷新(当缓存命中并发现二级缓存将要过期时,会开启一个异步线程刷新缓存)
  • 缓存Key支持SpEL表达式
  • Redis支持Kryo、FastJson,默认设置值(value)的序列化采用FastJsonRedisSerializer,设置键(key)的序列化采用StringRedisSerializer。
  • 支持同一个缓存名称设置不同的过期时间
  • 支持禁用一级缓存,只使用二级缓存
  • 通过允许存空值来解决缓存穿透问题

使用

直接在需要缓存的方法上加上CacheableCacheEvictCachePut注解。

  • Cacheable注解:
@Cacheable(value = "user:info", ignoreException = false,
        firstCache = @FirstCache(expireTime = 4, timeUnit = TimeUnit.SECONDS),
        secondaryCache = @SecondaryCache(expireTime = 10, preloadTime = 3,
            forceRefresh = true, timeUnit = TimeUnit.SECONDS))
public User getUserNoKey(long userId, String[] lastName) {
    logger.debug("测试没有配置key的缓存方法,参数是基本类型和数组的缓存缓存方法");
    User user = new User();
    user.setUserId(userId);
    user.setAge(31);
    user.setLastName(lastName);
    return user;
}
  • CachePut注解
@CachePut(value = "user:info", key = "#userId", ignoreException = false,
        firstCache = @FirstCache(expireTime = 4, timeUnit = TimeUnit.SECONDS),
        secondaryCache = @SecondaryCache(expireTime = 10, preloadTime = 3, forceRefresh = true, timeUnit = TimeUnit.SECONDS))
public User putUser(long userId) {
	User user = new User();
	user.setUserId(userId);
	user.setAge(31);
	user.setLastName(new String[]{"w", "y", "h"});

	return user;
}
  • CacheEvict注解
@CacheEvict(value = "user:info", key = "#userId")
public void evictUser(long userId) {
}

@CacheEvict(value = "user:info", allEntries = true)
public void evictAllUser() {
}

注解说明

@Cacheable

表示用的方法的结果是可以被缓存的,当该方法被调用时先检查缓存是否命中,如果没有命中再调用被缓存的方法,并将其返回值放到缓存中。

名称默认值说明
value空字符串数组缓存名称,cacheNames的别名
cacheNames空字符串数组缓存名称
key空字符串缓存key,支持SpEL表达式
depict空字符串缓存描述(在缓存统计页面会用到)
ignoreExceptiontrue是否忽略在操作缓存中遇到的异常,如反序列化异常
firstCache注解,一级缓存配置
secondaryCache注解,二级缓存配置

@FirstCache

一级缓存配置项

名称默认值说明
initialCapacity10缓存初始Size
maximumSize5000缓存最大Size
expireTime9缓存有效时间
timeUnitTimeUnit.MINUTES时间单位,默认分钟
expireModeExpireMode.WRITE缓存失效模式,ExpireMode.WRITE:最后一次写入后到期失效,ExpireMode.ACCESS:最后一次访问后到期失效

@SecondaryCache

二级缓存配置项

名称默认值说明
expireTime5缓存有效时间
preloadTime1缓存主动在失效前强制刷新缓存的时间,建议是 expireTime * 0.2
timeUnitTimeUnit.HOURS时间单位,默认小时
forceRefreshfalse是否强制刷新(直接执行被缓存方法)
isAllowNullValuefalse是否允许缓存NULL值
magnification1非空值和null值之间的时间倍率,默认是1。isAllowNullValue=true才有效
magnification1非空值和null值之间的时间倍率,默认是1。isAllowNullValue=true才有效

@CachePut

将数据放到缓存中

名称默认值说明
value空字符串数组缓存名称,cacheNames的别名
cacheNames空字符串数组缓存名称
key空字符串缓存key,支持SpEL表达式
depict空字符串缓存描述(在缓存统计页面会用到)
ignoreExceptiontrue是否忽略在操作缓存中遇到的异常,如反序列化异常
firstCache注解,一级缓存配置
secondaryCache注解,二级缓存配置

@CacheEvict

删除缓存

名称默认值说明
value空字符串数组缓存名称,cacheNames的别名
cacheNames空字符串数组缓存名称
key空字符串缓存key,支持SpEL表达式
allEntriesfalse是否删除缓存中所有数据,默认情况下是只删除关联key的缓存数据,当该参数设置成 true 时 key 参数将无效
ignoreExceptiontrue是否忽略在操作缓存中遇到的异常,如反序列化异常