redis 缓存
作用:将数据缓存在redis数据库中,快速响应客户端请求,短时间内数据不一致,适用于对数据一致性要求不高的场景
缓存使用条件:
频繁查询的数据,对使用不频繁的数据可不用缓存
内存空间较磁盘小,大数据不宜使用缓存
适用于读多写少的场景,如果频繁写入,也不适宜使用缓存
spring 默认策略:
数据查询:先查询缓存,如果缓存里面有数据,直接返回数据;如果没有执行方法,返回方法数据,并将返回结果放入缓存
数据更新:执行方法,将方法返回结果放入缓存
数据删除:删除后端数据库与缓存中的数据
spring 默认策略可能导致的问题
缓存一致性:后端数据库里存储最新的数据,缓存里面存储旧的数据
解决方法:后端数据更新后,延时删除缓存
缓存查询时,使用分布式锁让一个线程去后端获取最新数据放入缓存,其余线程从缓存里面获取数据
如果不延时删除,可能存在此种情况导致缓存不一致:线程1查询时缓存刚好失效,去后端查询数据,线程2更新数据并删除,此时线程1将就数据放入缓存
延时删除可能会失败,解决方式:消息队列(rocketmq)有重试机制,可使用消息队列删除缓存;
监听bin-log,使用消息队列删除对应的缓存,这种方式可减少对应用程序的侵入
**********************
相关注解
@Cacheable:查询
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Cacheable {
@AliasFor("cacheNames")
String[] value() default {};
@AliasFor("value")
String[] cacheNames() default {}; //指定使用缓存的名称
String key() default "";
String keyGenerator() default "";
String cacheManager() default "";
String cacheResolver() default "";
String condition() default "";
String unless() default "";
boolean sync() default false;
}
@CachePut:写入、更新
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface CachePut {
@AliasFor("cacheNames")
String[] value() default {};
@AliasFor("value")
String[] cacheNames() default {}; //指定使用缓存的名称
String key() default "";
String keyGenerator() default "";
String cacheManager() default "";
String cacheResolver() default "";
String condition() default "";
String unless() default "";
}
说明:将注解所在方法的返回值添加到缓存中,如果方法没有返回值且允许缓存空值,缓存的value为null
@CacheEvict:删除
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface CacheEvict {
@AliasFor("cacheNames")
String[] value() default {};
@AliasFor("value")
String[] cacheNames() default {};
String key() default "";
String keyGenerator() default "";
String cacheManager() default "";
String cacheResolver() default "";
String condition() default "";
boolean allEntries() default false;
boolean beforeInvocation() default false;
}
缓存键key设置
#使用keyGenerator
@Component("keyGenerator")
public class CustomKeyGenerator implements KeyGenerator {
@Override
public Object generate(Object o, Method method, Object... objects) {
StringBuilder builder=new StringBuilder();
builder.append(o.getClass().getName()).append("::").append(method.getName());
for(Object object:objects) {
builder.append(":").append(object.toString());
}
return builder.toString();
}
}
#示例
@Cacheable(value = "custom",keyGenerator = "keyGenerator")
@CachePut(value = "custom",keyGenerator = "keyGenerator")
@CacheEvict(value = "custom",keyGenerator = "keyGenerator")
*********************
#使用key
@Cacheable(value = "custom",key = "#id") //直接使用方法参数
public User getById(Integer id) {
@CachePut(value = "custom",key = "'user '+#user.name") //使用方法参数属性
public User save(User user) {
@CachePut(value = "custom",key = "'user '+#result.id") //使用方法返回结果属性
public User save(User user) {
@CacheEvict(value = "custom",key = "#id") //直接使用方法参数
public void delete(Integer id) {
**********************
示例
*****************
config 层
RedisCacheConfig
@Configuration
@EnableCaching
public class RedisCacheConfig {
@Bean
public CacheManager initCacheManager(RedisConnectionFactory connectionFactory){
RedisCacheConfiguration cacheConfiguration=RedisCacheConfiguration.defaultCacheConfig()
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new JdkSerializationRedisSerializer()))
.entryTtl(Duration.ofMinutes(2L))
.disableCachingNullValues();
RedisCacheConfiguration defaultCacheConfiguration=RedisCacheConfiguration.defaultCacheConfig()
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new JdkSerializationRedisSerializer()))
.entryTtl(Duration.ofMinutes(1L))
.disableCachingNullValues();
Map<String,RedisCacheConfiguration> map=new HashMap<>();
map.put("custom",cacheConfiguration);
map.put("default",defaultCacheConfiguration);
return RedisCacheManager.builder(connectionFactory)
.cacheDefaults(defaultCacheConfiguration)
.withInitialCacheConfigurations(map)
.transactionAware()
.build();
}
}
说明:配置多个缓存(default、custom),主要是为缓存设置不同的过期时间
CustomKeyGenerator:自定义key
@Component("keyGenerator")
public class CustomKeyGenerator implements KeyGenerator {
@Override
public Object generate(Object o, Method method, Object... objects) {
StringBuilder builder=new StringBuilder();
builder.append(o.getClass().getName()).append("::").append(method.getName());
for(Object object:objects) {
builder.append(":").append(object.toString());
}
return builder.toString();
}
}
*****************
service 层
CustomUserService
public interface CustomUserService {
User getById(Integer id);
User save(User user);
void delete(Integer id);
}
*****************
serviceImpl 层
CustomUserServiceImpl
@Service
public class CustomUserServiceImpl implements CustomUserService {
@Resource
private UserMapper userMapper;
@Override
@Cacheable(value = "custom",keyGenerator = "keyGenerator")
public User getById(Integer id) {
return userMapper.selectById(id);
}
@Override
@CachePut(value = "default",keyGenerator = "keyGenerator")
public User save(User user) {
userMapper.insert(user);
return user;
}
@Override
@CacheEvict(value = "custom",keyGenerator = "keyGenerator")
public void delete(Integer id) {
userMapper.deleteById(id);
}
}
*****************
controller 层
HelloController
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private CustomUserService userService;
@RequestMapping("/get/{id}")
public User get(@PathVariable("id") Integer id){
return userService.getById(id);
}
@RequestMapping("/save")
public User save(){
User user=new User();
user.setName("瓜田李下");
user.setAge(24);
userService.save(user);
return user;
}
@RequestMapping("/delete")
public User delete(){
User user=userService.getById(2);
userService.delete(user.getId());
return user;
}
}
**************************
使用测试
localhost:8080/user/save