Redis缓存

195 阅读2分钟

缓存原理图

image.png

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);
    }
}

企业级缓存简单使用

image.png

定义服务接口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;
    }

}

缓存穿透:恶意攻击去查询数据库中没有的数据,对数据库造成压力,甚至压垮数据库

解决方法:使用特殊缓存空值标识对象不存在

缓存雪崩:在某一个时间段,缓存集中过期失效,对于数据库而言就会产生周期性的压力波峰

解决方法:设置过期时间加上一个随机因子,尽可能的分散缓存过期时间