缓存原理图
reids介绍
- 完全开源免费
- 支持数据持久化
- 支持多种数据结构
- 支持数据备份(master-slave)
reids-start使用
spring-boot-starter-data-redis jar包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
application.properties定义redis相关属性
#redis的配置
spring.redis.port=8379
spring.redis.password=qwer1234
spring.redis.host=127.0.0.1
spring.redis.database=0
RedisConfig注入自定义RedisTemplate
@Configuration
public class RedisConfiguration {
@Bean
public RedisTemplate<String,Object> redisTemplate(LettuceConnectionFactory factory){
RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setValueSerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new StringRedisSerializer());
redisTemplate.setConnectionFactory(factory);
return redisTemplate;
}
}
编写RedisUtil使用redisTemplate进行CRUD
@Component
public class RedisUtil {
@Autowired
private RedisTemplate redisTemplate;
public boolean set(String key,Object value){
try {
redisTemplate.opsForValue().set(key,value);
return true;
}catch (Exception e){
e.printStackTrace();
return false;
}
}
public boolean set(String key,Object value,long time){
try {
redisTemplate.opsForValue().set(key,value,time, TimeUnit.SECONDS);
return true;
}catch (Exception e){
e.printStackTrace();
return false;
}
}
public Object get(String key){
return Optional.ofNullable(redisTemplate.opsForValue().get(key)).orElse(null);
}
}
企业级缓存简单使用
定义服务接口API
public interface LabelService {
Label selectById(int id);
}
@Service
public class LabelServiceImpl implements LabelService {
@Resource(name = "labelRepositoryCacheImpl")
private LabelRepository labelRepository;
@Override
public Label selectById(int id) {
return Optional.ofNullable(labelRepository.selectById(id)).orElse(null);
}
}
定义仓储层接口
public interface LabelRepository {
Label selectById(int id);
}
@Component
public class LabelRepositoryImpl implements LabelRepository {
@Autowired
private LabelMapper labelMapper;
@Override
public Label selectById(int id) {
return labelMapper.selectByPrimaryKey(id);
}
}
仓储层实现
@Component
public class LabelRepositoryCacheImpl implements LabelRepository {
private static final String CACHE_PREFIX = "cache_prefix";
@Autowired
private RedisUtil redisUtil;
@Resource(name = "labelRepositoryImpl")
private LabelRepository labelRepository;
private Label nullLabel = new Label(-1);
private static final Random random = new Random();
@Override
public Label selectById(int id) {
Label label = getLabelFromCache(id);
if (label == null){
System.out.println("cache not hit");
//从持久层拿
label = labelRepository.selectById(id);
cacheLabel(label,id);
}else if (label.getId() == -1){
System.out.println("cache hit null");
return null;
}else {
System.out.println("cache hit id");
}
return label;
}
private void cacheLabel(Label res,int id) {
if (res != null){
//写入缓存中
//random.nextInt(5) 防止一个缓存雪崩
redisUtil.set(generateCacheKey(id),JSON.toJSONString(res),10 + random.nextInt(5));
}else {
//防止缓存穿透
redisUtil.set(generateCacheKey(id),JSON.toJSONString(nullLabel),10 + random.nextInt(5));
}
}
private Label getLabelFromCache(int id) {
//从缓存中获取
String label = (String) redisUtil.get(generateCacheKey(id));
//如果没有
if (StringUtils.isEmpty(label)){
return null;
}
return JSON.parseObject(label,Label.class);
}
private static String generateCacheKey(int id){
return CACHE_PREFIX + id;
}
}
缓存穿透:恶意攻击去查询数据库中没有的数据,对数据库造成压力,甚至压垮数据库
解决方法:使用特殊缓存空值标识对象不存在
缓存雪崩:在某一个时间段,缓存集中过期失效,对于数据库而言就会产生周期性的压力波峰
解决方法:设置过期时间加上一个随机因子,尽可能的分散缓存过期时间