【为什么使用redis
- 性能极高,redis能读的速度是110000次/s,写的速度是81000次/s
- 丰富的数据类型,redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作
- redis命令友好易用
- springboot 已经自动集成了redis
【redis配置
1.首先在build.gradle中引入redis的依赖:
compile('org.springframework.boot:spring-boot-starter-data-redis')
其实做完这一步我们已经可以直接使用springboot提供的RedisTemplate,但是我们需要进一步优化,并且使用注解配置缓存
2.添加缓存配置类:
- KeyGenerator表明我们自己定义key生成的策略
- RedisCustomSerializer表明我们自己定义序列化的方式,这里使用了protostuff来序列化,protostuff是目前最高效,节省空间的序列化方式
3.在springboot启动类上表明启用缓存:@EnableCaching
4.定义缓存的名称集合,统一管理缓存名称
5.在需要使用缓存的查询服务上使用:@Cacheable(keyGenerator = "keyGenerator")
6.在需要清理缓存的业务服务上使用:@CacheEvict(keyGenerator = "keyGenerator")
【缓存配置类
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.lang.Nullable;
import java.time.Duration;
/**
* redis缓存配置类
* @author ibm
* @since 0
* @date 2018-4-12
*/
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
@Override
@Nullable
@Bean
public KeyGenerator keyGenerator() {
return new RedisCustomKeyGenerator();
}
@Bean
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
StringRedisTemplate template = new StringRedisTemplate(factory);
RedisCustomSerializer customSerializer = new RedisCustomSerializer();
template.setValueSerializer(customSerializer);
template.afterPropertiesSet();
return template;
}
/**
* 设置 redis 数据默认过期时间
* 设置@cacheable 序列化方式
* @return
*/
@Bean
public RedisCacheConfiguration redisCacheConfiguration(){
RedisCustomSerializer customSerializer = new RedisCustomSerializer();
RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig();
configuration = configuration.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer
(customSerializer)).entryTtl(Duration.ofHours(1));
return configuration;
}
}
【自定义序列化
import com.example.seckill.dao.entity.KillProduct;
import io.protostuff.LinkedBuffer;
import io.protostuff.ProtostuffIOUtil;
import io.protostuff.runtime.RuntimeSchema;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import org.springframework.lang.Nullable;
/**
* 自定义的redis序列化
* @author ibm
* @since 0
* @date 2018-4-22
*/
public class RedisCustomSerializer implements RedisSerializer {
private final RuntimeSchema<KillProduct> schema = RuntimeSchema.createFrom(KillProduct.class);
@Nullable
@Override
public byte[] serialize(@Nullable Object o) throws SerializationException {
KillProduct killProduct = (KillProduct)o;
byte[] bytes = ProtostuffIOUtil.toByteArray(killProduct,schema,
LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE));
return bytes;
}
@Nullable
@Override
public Object deserialize(@Nullable byte[] bytes) throws SerializationException {
if(bytes != null){
KillProduct killProduct = schema.newMessage();
//反序列化
ProtostuffIOUtil.mergeFrom(bytes,killProduct,schema);
return killProduct;
}else {
return null;
}
}
}
【自定义key生成策略
这里有一个不好的地方是我直接使用第一个参数作为key的标示,是的程序中必须将id放在第一位,但这里只是一个事例,表明我们的key可以在这里进行自定义。
import org.springframework.cache.interceptor.KeyGenerator;
import java.lang.reflect.Method;
/**
* 自定义的redis缓存key生成策略
* @author ibm
* @since 0
* @date 201804013
*/
public class RedisCustomKeyGenerator implements KeyGenerator {
/**
* 简单的指定生成killProduct的缓存id,这里可以根据业务类型自定义所有的key生成策略
* @param target 被调用方法的类实例
* @param method 方法的名称
* @param params 方法的参数
* @return 缓存key
*/
@Override
public Object generate(Object target, Method method, Object... params) {
return params[0];
}
/**
* 提供redisTemplate使用的key查询方法
* @param cacheName 缓存名称
* @return 缓存的key前缀
*/
public static final String getKey4CacheName(String cacheName){
//spring在生成key的时候会用cacheName::的前缀
return cacheName + "::";
}
}
【使用spring注解操作缓存
- 在使用的类(读与写的类都需要)上我们使用如下注解表明这个服务使用缓存的名称是什么,也可以直接在方法上指明cacheName但是要写多次。
@CacheConfig(cacheNames = RedisCacheName.KILL_PRODUCT) - 在查询的服务方法上添加如下注解表明该方法的返回值需要缓存。
@Cacheable(keyGenerator = "keyGenerator") - 当被缓存的数据发生改变,缓存需要被清理或者修改,这里使用如下注解清除指定key的缓存。
@CacheEvict(keyGenerator = "keyGenerator")
【redis客户端查看缓存
使用redis-cli命令进入redis(docker exec -it containerId redis-cli)
输入keys * 查看所有的缓存
我们可以看见缓存是按照cacheName + "::" + id 的方式生成的,而我们的key生成策略也是针对于生成id的那一部分。
【值得注意的一点
我们在使用缓存的时候应该注意缓存的对象应该处于哪一层,试想如果我的缓存在dao这一层,但是事务在service层,一个service方法包含了多个dao方法,如果在执行service方法的时候,拥有缓存的dao方法成功,但是接下来的到方法失败,那么我们的缓存就生效了,但是数据并没有落库,这就产生了数据不一致的问题。所以我们的缓存应该在事务的更上层。事务是一个原子操作,所有的缓存,消息,这种非强一致性要求的操作,都应该在事务成功提交后执行。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。