Redis 有两个模板: RedisTemplate 和 StringRedisTemplate. 不推荐使用 RedisTemplate, 因为 RedisTemplate 提供的是操作对象, 而我们通常以 JSON 格式存储该对象, 存储时会使用 Redis 默认的内部序列化器, 容易导致存储内容出现乱码. 此时需要我们自定义序列化.
StringRedisTemplate 为我们提供了字符串操作, 将实体类转换成 JSON 字符串进行存储, 等取出来后, 再将其转换成相应的对象
pom.xml
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-data-Redis</artifactId>
- </dependency>
application.YAML
- spring
- Redis:
- # open: false # 是否开启 Redis 缓存 true 开启 false 关闭
- database: 0
- host: 127.0.0.1
- port: 6379
- password:
- timeout: 6000 # 连接超时时长 (毫秒)
- pool:
- max-active: 1000 # 连接池最大连接数 (使用负值表示没有限制)
- max-wait: -1 # 连接池最大阻塞等待时间 (使用负值表示没有限制)
- max-idle: 10 # 连接池中的最大空闲连接
- min-idle: 5 # 连接池中的最小空闲连接
- RedisConfig
使用 StringRedisTemplate 可以不重写 keyGenerator 方法
- /**
- * Redis 配置
- *
- * @Author YangXuyue
- * @Date 2018/08/02 21:57
- */
- @Configuration
- @EnableCaching
- @AutoConfigureAfter(RedisAutoConfiguration.class)
- public class RedisConfig extends CachingConfigurerSupport {
- /*
- 缓存命中率
- 即从缓存中读取数据的次数 与 总读取次数的比率, 命中率越高越好:
- 命中率 = 从缓存中读取次数 / (总读取次数 [从缓存中读取次数 + 从慢速设备上读取的次数])
- Miss 率 = 没有从缓存中读取的次数 / (总读取次数 [从缓存中读取次数 + 从慢速设备上读取的次数])
- 这是一个非常重要的监控指标, 如果做缓存一定要健康这个指标来看缓存是否工作良好
- */
- /*
- 缓存策略
- Eviction policy
- 移除策略, 即如果缓存满了, 从缓存中移除数据的策略; 常见的有 LFU,LRU,FIFO:
- FIFO(First In First Out): 先进先出算法, 即先放入缓存的先被移除;
- LRU(Least Recently Used): 最久未使用算法, 使用时间距离现在最久的那个被移除;
- LFU(Least Frequently Used): 最近最少使用算法, 一定时间段内使用次数 (频率) 最少的那个被移除;
- TTL(Time To Live )
- 存活期, 即从缓存中创建时间点开始直到它到期的一个时间段 (不管在这个时间段内有没有访问都将过期)
- TTI(Time To Idle)
- 空闲期, 即一个数据多久没被访问将从缓存中移除的时间
- */
- /*
- Spring 提供了 Cache:org.springframework.cache; 该 Cache 提供了缓存操作的读取 / 写入 / 移除方法
- 因为应用并不是只有一个 Cache, 所以 Spring 提供了 CacheManager 抽象, 用于缓存管理
- Cache 和 CacheManager 都有其默认实现 (GuavaXXX,EhCacheXXX...)
- GuavaCacheManager 之外, 其他 Cache 都支持 Spring 事务的, 即如果事务回滚了, Cache 的数据也会移除掉.
- */
- /*
- 继承 CachingConfigurerSupport, 实现后注入需要的 cacheManager 和 keyGenerator
- */
- // 设置 RedisCacheManager. 在配置 CacheManager 的方法中, 也可以配置缓存默认的过期时间.
- @Bean
- public CacheManager cacheManager(RedisTemplate redisTemplate) {
- RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
- // 设置缓存过期时间, 单位秒
- Map<String, Long> expiresMap = new HashMap<>();
- expiresMap.put(CacheNames.MASTER, 600L);
- expiresMap.put(CacheNames.SECOND, 600L);
- // 设置超时
- cacheManager.setExpires(expiresMap);
- // TODO 没有设置的缓存默认过期时间
- //cacheManager.setDefaultExpiration(60 * 60L);
- return cacheManager;
- }
- @Bean
- public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory) {
- StringRedisTemplate template = new StringRedisTemplate(factory);
- Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
- ObjectMapper om = new ObjectMapper();
- om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
- om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
- jackson2JsonRedisSerializer.setObjectMapper(om);
- template.setValueSerializer(jackson2JsonRedisSerializer);
- template.afterPropertiesSet();
- return template;
- }
- /**
- * Redis 设置一些全局配置, 比如配置主键的生产策略 KeyGenerator, 如不配置会默认使用参数名作为主键.
- *
- * @return
- */
- @Override
- @Bean
- public KeyGenerator keyGenerator() {
- return new BaseKeyGenerator();
- }
- /**
- * Redis 主键生成策略
- */
- private class BaseKeyGenerator implements KeyGenerator {
- @Override
- public Object generate(Object target, Method method, Object... params) {
- // 目标 Object 的名称 + 方法名 + Params 各个元素名称
- StringBuilder sb = new StringBuilder();
- sb.append(target.getClass().getSimpleName());
- sb.append(".").append(method.getName());
- for (Object obj : params) {
- if (null != obj) {
- sb.append(obj.toString());
- }
- }
- return sb.toString();
- }
- }
- }
注解使用例子
- @Service
- @CacheConfig(cacheNames = {CacheNames.MASTER})
- public class UserServiceImpl implements UserService {
- /*
- @CacheConfig: 主要用于配置该类中会用到的一些共用的缓存配置. 在这里 @CacheConfig(cacheNames = {CacheNames.MASTER}):
- 配置了该数据访问对象中返回的内容将存储于名为 users 的缓存对象中, 我们也可以不使用该注解, 直接通过 @Cacheable 自己配置缓存集的名字来定义.
- */
- @Autowired
- private UserRepository repository;
- /*
- 保存的值为返回值, 注解方面再研究研究. 合理使用缓存的 key, 不然会没有效果
- */
- /*
- @CachePut 效果与 @Cacheable 一样
- 因为 @CachePut 是方法执行完才生效, 所以当新增一个 user 的时候, user 的 id 是有值的
- 这里只针对 user 的 id 做了 user 的缓存, 如果 findByUsername 也做缓存, 那么数据将会出现不一致的情况
- 因为 key 值不一样, 会更新不到以 username 为 key 信息的缓存
- */
- /*
- 缓存注解详解
- @Cacheable: 配置了 findByName 函数的返回值将被加入缓存. 同时在查询时, 会先从缓存中获取, 若不存在才再发起对数据库的访问. 该注解主要有下面几个参数:
- value,cacheNames: 两个等同的参数 (cacheNames 为 Spring 4 新增, 作为 value 的别名),
- 用于指定缓存存储的集合名. 由于 Spring 4 中新增了 @CacheConfig, 因此在 Spring 3 中原本必须有的 value 属性, 也成为非必需项了
- key: 缓存对象存储在 Map 集合中的 key 值, 非必需, 缺省按照函数的所有参数组合作为 key 值, 若自己配置需使用 SpEL 表达式, 比如:@Cacheable(key = "#p0"):
- 使用函数第一个参数作为缓存的 key 值, 更多关于 SpEL 表达式的详细内容可参考官方文档
- condition: 缓存对象的条件, 非必需, 也需使用 SpEL 表达式, 只有满足表达式条件的内容才会被缓存,
- 比如:@Cacheable(key = "#p0", condition = "#p0.length() < 3"), 表示只有当第一个参数的长度小于 3 的时候才会被缓存,
- 若做此配置上面的 AAA 用户就不会被缓存, 读者可自行实验尝试.
- unless: 另外一个缓存条件参数, 非必需, 需使用 SpEL 表达式. 它不同于 condition 参数的地方在于它的判断时机,
- 该条件是在函数被调用之后才做判断的, 所以它可以通过对 result 进行判断.
- keyGenerator: 用于指定 key 生成器, 非必需. 若需要指定一个自定义的 key 生成器,
- 我们需要去实现 org.springframework.cache.interceptor.KeyGenerator 接口, 并使用该参数来指定. 需要注意的是: 该参数与 key 是互斥的
- cacheManager: 用于指定使用哪个缓存管理器, 非必需. 只有当有多个时才需要使用
- cacheResolver: 用于指定使用那个缓存解析器, 非必需. 需通过 org.springframework.cache.interceptor.CacheResolver 接口来实现自己的缓存解析器, 并用该参数指定.
- @CachePut: 配置于函数上, 能够根据参数定义条件来进行缓存, 它与 @Cacheable 不同的是, 它每次都会真是调用函数, 所以主要用于数据新增和修改操作上.
- 它的参数与 @Cacheable 类似, 具体功能可参考上面对 @Cacheable 参数的解析
- @CacheEvict: 配置于函数上, 通常用在删除方法上, 用来从缓存中移除相应数据. 除了同 @Cacheable 一样的参数之外, 它还有下面两个参数:
- allEntries: 非必需, 默认为 false. 当为 true 时, 会移除所有数据
- beforeInvocation: 非必需, 默认为 false, 会在调用方法之后移除数据. 当为 true 时, 会在调用方法之前移除数据.
- */
- @Override
- @Transactional(rollbackFor = Exception.class)
- @CachePut(key = "'UserService_UserId_'+#user.id")
- public User save(User user) {
- return repository.save(user);
- }
- @Override
- @Cacheable(key = "'UserService_UserId_'+#id")
- public User findById(Long id) {
- return repository.findById(id);
- }
- @Override
- @Transactional(rollbackFor = Exception.class)
- @CacheEvict(key = "'UserService_UserId_'+#id")
- public void deleteById(Long id) {
- repository.delete(id);
- }
- }
- public interface CacheNames {
- /**
- * 主缓存名称
- */
- String MASTER = "master";
- String SECOND = "second";
- }
来源: http://www.bubuko.com/infodetail-2861042.html