关于redis:缓存

50次阅读

共计 1864 个字符,预计需要花费 5 分钟才能阅读完成。

一、缓存的应用

  • 即时性、数据一致性要求不高的
  • 访问量大且更新频率不高的数据(读多,写少)
    举例:电商利用,商品分类,商品列表等适宜缓存并加一个生效工夫(依据数据更新频率来定),后盾如果公布一个商品,买家须要 5 分钟能力看到新的商品个别还是能够承受的

二、高并发下缓存生效问题

  1. 缓存穿透
    查问一个不存在的数据,因为缓存是不命中的,将去查询数据库,然而数据库也无此记录,咱们没有将这个此查问的 null 写入缓存,这将导致这个不存在的数据每次申请都到存储层查问,失去了缓存的意义。
    危险:利用不存在的数据进行攻打,数据库刹时压力增大,最终导致解体
    解决:null 后果缓存,并退出短暂过期工夫
  2. 缓存雪崩
    指在咱们设置缓存时 key 采纳了雷同的过期工夫,导致缓存在某一时刻同时生效,申请全副转发到 DB,DB 刹时压力过重雪崩。
    解决:原有生效工夫的根底上减少一个随机值,比方 1 - 5 分钟随机,这样没有一个缓存的过期工夫的反复率就会升高,就很难引发个体生效事件。
  3. 缓存击穿
    对于设置过期工夫的 key,如果这些 key 可能会在某些工夫点被高并发地拜访,是一种十分 ” 热点 ” 的数据。
    如果这个 key 在大量申请同时进来前正好生效,那么所有对这个 key 的数据查问都落到 DB,咱们称为缓存击穿。
    解决:加锁,大量并发只让一个去查,其他人期待,查到后开释锁,其他人获取到锁,先查缓存,就会有数据,不必去 DB

三、本地锁

 只有是同一把锁,就能锁住须要这把锁的所有线程,

1. synchronized(this),JUC(lock)

SpringBoot 所有的组件在容器中都是单例的,一个服务一个容器,一个容器一个实例,

每个 this 是不同的锁,

四、分布式锁

基本原理: 能够去一个中央占坑,占到就执行逻辑,否则期待,直到开释锁,” 占坑 ” 能够去 redis,能够去数据库,能够去任何大家都能拜访到的中央,期待能够自旋的形式。
问题

  1. setnx 占好了位,业务异样或者程序在页面过程中宕机,没有执行删除锁逻辑,就造成了死锁,能够通过设置锁的主动过期,即便没有删除,会主动删除,设置过期和加锁要原子操作
  2. 如果业务工夫长,锁本人过期了,咱们间接删除,有可能把他人正在持有的锁删除了,占锁的时候指定 uuid,每个人匹配本人的锁才删除。删除之前判断是否是本人的

     String lockValue = stringRedisTemplate.opsForValue().get("lock");
     if (uuid.equals(lockValue)) {stringRedisTemplate.delete("lock");
     }
    

但这样还是有问题,从 redis 取出值,返回的路上,redis 过期了,别的线程加锁胜利,这时判断为 true,删除就删除了别的线程的锁,所以获取,判断,删除要是原子操作,通过 lua 脚本来实现。

Redisson

  • 阻塞式期待,默认加锁都是 30s 工夫
  • 锁的主动续期,如果业务超长,运行期间主动给锁续上新的 30s, 不必放心业务工夫长,锁主动过期被删掉
  • 加锁业务只有运行实现,就不会给以后锁续期,即便不手动解锁,锁默认在 30 秒当前主动删除
  1. 读写锁
    保障肯定能读到最新数据,批改期间,写锁是一个排他锁(互斥锁、独享锁), 读锁是一个共享锁
    写锁没开释读就必须期待
    读 + 读:相当于无锁
    写 + 读:期待写锁开释
    写 + 写:阻塞形式
    读 + 写:有读锁,写也须要期待
    只有有写的存在,都必须期待

    五、缓存一致性

    缓存中的数据如何与数据库保持一致(缓存数据一致性)

    1. 双写模式
      批改了数据库再查下,保留至缓存。两个线程批改同一条数据库,因为卡顿起因,导致写缓存 2 在最前,写缓存 1 在前面就呈现不统一。
      脏数据问题:这时暂时性脏数据问题,然而在数据稳固,缓存过期后,又能失去最新的正确数据(最终一致性)
    2. 生效模式
      批改数据库,删除缓存

无论双写还是生效模式,都会导致缓存不统一问题,即多个实例同时更新会出事,怎么办?

  1. 如果是用户维度数据(订单数据、用户数据),这种并发几率小,不必思考这个问题,缓存数据加上过期工夫,每隔一段时间触发读被动更新即可
  2. 如果是菜单,商品介绍等根底数据,也能够去应用 canal 订阅 binlog 的形式。
  3. 缓存数据 + 过期工夫也足够解决大部分业务对于缓存的要求
  4. 通过加锁保障并发读写,写写的时候按程序排好队,读读无所谓,所以适宜读写锁(业务不关心脏数据,容许长期脏数据可疏忽)

总结

  • 咱们能放入缓存的数据本就不应该是实时性的,一致性要求超高的,所以缓存数据加上过期工夫,保障每天拿到以后的最新数据即可
  • 咱们不应该适度设计,减少零碎的复杂性
  • 遇到实时性、一致性要求高到的数据,就应该查数据库,即便慢点

正文完
 0