缓存

    高并发下缓存失效问题
1、缓存穿透
    查询一个一定不存在的数据,由于缓存不会命中,将去数据库查询,但是数据库也无此记录,我们没有将这次查询的Null写入缓存,就导致这个不存在的数据每次请求都要到数据库去查询,失去了缓存的意义
    
    风险: 利用不存在的数据进行攻击,数据库瞬时压力增大,最终导致崩溃
    解决:null结果缓存,并加入短暂过期时间
    
2、缓存雪崩
    缓存时key采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到db,DB瞬时压力过大崩溃
    解决:原有的失效时间基础上增加一个随机值,这样每一个缓存的过期时间的重复率就会降低,很难引起机体失效的事件
    
3、缓存击穿
    对于一些设置了过期时间的key,如果这些key可能会在某个时间点被超高并发访问,是一种非常热点的数据。如果这个key在大量请求同时进来之前刚好失效,那么所有对这个key的数据查询都会去查数据库,称之为缓存击穿
    解决:大量并发只让一个去查,其他人等待,查到以后释放锁,其他人获取到锁,先查询缓存,就会有数据,不会去查db

 

读写锁
    保证一定能读到最新数据,修改期间,写锁时一个排他锁,读锁时一个共享锁
    写锁未释放,读锁就必须等待
    读+读:相当于无锁,并发读,指挥在redis中记录所有当前的读锁,他们都会同时加锁成功
    写+读:读需要等待写锁释放
    写+写:阻塞
    读+写:有读锁,写锁必须等待读锁释放
    只要有写的存在,就必须等待
    
    

缓存数据一致性---双写模式
    双写模式:写数据库的同时更新缓存,会有并发问题,解决办法:lock--写数据库--写缓存--unlock
    失效模式:1、写数据库的同时删除缓存,等业务来拉取时自己更新数据
             2、给缓存中的数据设置有效期,有效期一到会自动去数据库更新数据

缓存一致性解决方案
    无论是双写模式还是失效模式,都会导致缓存的不一致问题,即多个实例同时更新,怎么办?
        1、如果是用户维度数据(订单数据、用户数据),并发几率非常小,可以无视,缓存数据加过期时间,每隔一段时间出发读的主动更新即可
        2、如果是菜单、商品介绍等基础数据,也可以使用canal订阅binlog的方式
        3、缓存数据+过期时间足够解决大部分业务对于缓存的要求
        4、通过加锁保证并发读写,写写的时候按顺序排队,读读无所谓,所以适合使用读写锁(业务不关心脏数据,允许临时脏数据可忽略)
        
    
总结:
    1、我们能放入缓存的数据本就不应该是实时性、一致性要求超高的,所以缓存数据的时候加上过期时间,保证每天拿到当前的最新数据即可
    2、不应该过度设计,增加系统的复杂性、
    3、遇到实时性、一致性要求高的数据,就应该差数据库,即使慢点 

整合SpringCache简化缓存开发
    1、引入依赖 spring-boot-starter-cache, spring-boot-starter-data-redis
    2、写配置
        1、自动配置了哪些
            CacheAutoConfiguration会导入RedisCacheConfiguration
            自动配置好了缓存管理器RedisCacheManager
        2、配置使用redis作为缓存    spring.cache.type=redis
    3、测试使用缓存
        @Cacheable: Triggers cache population.  触发将数据保存到缓存的操作
        @CacheEvict: Triggers cache eviction.   出发将数据从缓存中移除的操作
        @CachePut: Updates the cache without interfering with the method execution. 不影响方法执行更
        @Caching: Regroups multiple cache operations to be applied on a method. 组合以上多个操作
        @CacheConfig: Shares some common cache-related settings at class-level. 在类级别共享缓存的相
        1、开启缓存功能 @EnableCaching
        2、只需要使用注解就能完成缓存操作
    4、原理
        CacheAutoConfiguration -> RedisCacheConfiguration -> 自动配置了RedisCacheManager ->初始化所有的缓存配置
        -> 每个缓存决定使用什么配置 ->如果redisCacheConfigureation有就用自己的,没有就用默认配置 -> 想改缓存配置,只需给容器中放一个RedisCacheConfiguraction即可 -> 就会应用到当前RedisCacheManager管理的所有缓存分区中 

//Cacheable放在查询方法上,key为缓存的key
@Cacheable(value = {"category"}, key = "#root.method.name")

//CacheEvict放在修改、删除的方法上
//key为删除的缓存Key,但此写法只能删除单条缓存
@CacheEvict(value = "category", key = "#root.method.name")
//删除多条缓存的写法(allEntries=true 删除value下的所有缓存)
	1、@CacheEvict(value = "category", allEntries = true)
	2、@Caching(evict = {
			@CacheEvict(value = "category", key = "'listTree'"),
			@CacheEvict(value = "category", key = "'list'")

/**
*  级联更新所有关联的数据
*    @CacheEvict 失效模式
*     1、同时进行多种缓存操作 @Caching
      2、指定删除某个分区下的所有数据 @CacheEvict(value="category", allEntities=true)
      3、存储同一类型的数据,都可以指定成同一个分区,缓存前缀不在配置文件中指定,使用默认的分区名作为前缀
*/

Spring Cache的不足
    1、读模式:
        缓存击穿: 解决方案,加锁, Cacheable加属性sync=true(加本地锁,get方法上加synchronized,并非分布式锁)
        缓存穿透: 解决方案:缓存空数据 cache-null-values=true
        缓存雪崩: 加随机时间,加过期时间 spring.cache.redis.time-to-live=3600000
    2、写模式
        1、读写锁
        2、引入Canal,感知到Mysql的更新去更新缓存
        3、读多写少,直接去查询数据库
    总结:常规数据(读多写少、及时性、一致性要求不高的数据)的使用,完全可以使用springcache,写模式(只要缓存的数据有过期时间就够了)
         特殊数据,特殊设计

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值