springboot连接redis
使用redisTemplate该类可以存放任意类型的数据,但是该类型的数据
必须实现序列,获取redis中对应的数据时,会进行反序列化。 如果
使用RedisTemplate建议大家指定key,value,以及hashkey的序列
化方式

配置 配置类

@EnableCaching
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
   @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        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);
        // 配置序列化(解决乱码的问题),过期时间600秒
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(600)) //缓存过期10分钟 ---- 业务需求。
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))//设置key的序列化方式
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)) //设置value的序列化
                .disableCachingNullValues();
        RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .build();
        return cacheManager;
    }
}
redis使用场景
(1)作为缓存
1. 减少数据库的访问频率。 提高数据的访问率。
2. 这些数据可以放入缓存:
     1.热点数据。
     2.修改频率比较低
     3.安全系数低的。

缓存穿透

(1)、 缓存穿透是指缓存和数据库中都没有的数据,⽽⽤户不断发起请求,我们数据库的 id 都是1开始⾃增上去的,如发起为id值为 -1
 的数据或 id 为特别⼤不存在的数据。这时的⽤户很可能是攻击者,攻击会导致数据库压⼒过⼤,严重会击垮数据库
 解决:①:修改一下业务系统的代码,将数据库查询结果为空的key也存储在缓存中。当后续又出现该key的查询请求时,缓存直接返回null,
 而无需查询数据库。
 ②:使用bloomFilter   
 			bloomFilter的原理:当⼀个元素被加⼊集合时,通过K个散列函数将这个元素映射成⼀个位数组中的K个点,把它们置为1。检
 			索时,我们只要看看这些点是不是都是1就(⼤约)知道集合中有没有它了:如果这些点有任何⼀个0,则被检元素⼀定不在;
 			如果都是1,则被检元素很可能在。这就是布隆过滤器的基本思想
	解决:当业务系统有查询请求的时候,首先去BloomFilter中查询该key是否存在。若不存在,则说明数据库中也不存在该数据,因此缓存
	都不要查了,直接返回null。若存在,则继续执行后续的流程,先前往缓存中查询,缓存中没有的话再前往数据库中的查询

缓存雪崩

缓存雪崩就是某一时刻,缓存中没有该记录或缓存过期,而这时会有大量的请求请求这些数据,最后这些请求会打压到数据库,数据库抗不住
解决:在批量往Redis存数据的时候,把每个Key的失效时间都加个随机值,这样可以保证数据不会在同⼀时间⼤⾯积失效

(2)缓存原理

springBoot 删除 Redis zset键值 springboot删除redis缓存_缓存

如何使用缓存

(1)搭建一个springboot+mp的工程 

 (2)引入redis相关的依赖

 (3)配置redis

  (4)service代码

代码实现:

@Service
public class DeptService {

    @Resource
    private DeptDao deptDao;

    @Autowired
    private RedisTemplate redisTemplate;

    public Dept findById(Integer deptId){
        //1.从缓存中查询该数据
        Object o = redisTemplate.opsForValue().get("findById::" + deptId);
        if(o!=null){//表示从缓存中获取该数据
            return (Dept) o;
        }
        Dept dept = deptDao.selectById(deptId);
        redisTemplate.opsForValue().set("findById::"+deptId,dept);//把查询的结果放入缓存
        return dept;
    }


    //数据库和缓存同步问题!
    public int delete(Integer deptId){
        redisTemplate.delete("findById::"+deptId);//删除缓存
        int i = deptDao.deleteById(deptId);
        return i;
    }

    public int update(Dept dept){
        redisTemplate.delete("findById::"+dept.getDeptId());//删除缓存
        int i = deptDao.updateById(dept);
        redisTemplate.opsForValue().set("findById::"+dept.getDeptId(),dept);
        return i;
    }
}

修改上面代码(每次都要写很多与业务无关的一些非业务代码)

(1)使用aop来解决---->动态代理(动态代理的实现模式基于
 JDK动态代理)
(2)基于spring的缓存注解。

实现:

@SpringBootApplication
@MapperScan(basePackages = "com.ganin.dao")
@EnableCaching //开启缓存的注解
public class SpringbootRedis02Application {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootRedis02Application.class, args);
    }

}
@Service
public class DeptService {

    @Resource
    private DeptDao deptDao;

    //该注解作用:会先查询缓存,如果缓存存在,则不会执行代码块。 如果缓存中不存在则执行该方法,并把该方法的返回值存放到redis中
    @Cacheable(cacheNames = "findById",key = "#deptId")  //缓存的key值 为findById
    public Dept findById(Integer deptId){
        System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
        Dept dept = deptDao.selectById(deptId);
        return dept;
    }


    //数据库和缓存同步问题!
    // beforeInvocation:是否在方法执行前就清空,缺省为 false,
    // 如果指定为 true,则在方法还没有执行的时候就清空缓存。缺省情况下,如果方法执行抛出异常,则不会清空缓存。
    @CacheEvict(cacheNames = "findById",key = "#deptId")
    public int delete(Integer deptId){
        int i = deptDao.deleteById(deptId);
        return i;
    }

    //这个注解是必须执行方法体,而且会把方法体执行的结果放入到缓存中。 如果发生异常则不操作缓存。
    @CachePut(cacheNames = "findById",key = "#dept.deptId")
    public Dept update(Dept dept){
        int i = deptDao.updateById(dept);
        return dept;
    }
}
(2)redis作为分布式锁

步骤:
加入redssion依赖 解决分布式问题

(1)配置类

@Bean
    public RedissonClient getRedisson(){
        Config config=new Config();
        config.useSingleServer().setAddress("redis://192.168.31.243:6379");
        RedissonClient redissonClient = Redisson.create(config);
        return redissonClient;
    }

(2)
实现

@Service
public class StockService {

    @Resource
    private StockDao stockDao;

    @Autowired
    private RedissonClient redisson; 

    public String decrStock(Integer productId) {//synchronized () 同步方法    同步代码块
        RLock lock = redisson.getLock("product::" + productId);//获取锁对象
        try {
            lock.tryLock(60,20,TimeUnit.SECONDS); //自己别设置时间。
            Stock stock = stockDao.selectById(productId);
            if (stock.getNum() > 0) {
                //根据id修改库存
                stock.setNum(stock.getNum() - 1);
                stockDao.updateById(stock); //异常发生
//                   int c=10/0;
//                Thread.sleep(35000);
                System.out.println("库存剩余:" + (stock.getNum()));
                return "库存减少成功";
            } else {
                return "库存不足";
            }
        }catch (Exception e){
             throw  new RuntimeException(e.getMessage());
        }
        finally {
            lock.unlock();
        }
    }
}