2-本地缓存,分布式缓存,数据一致性,分布式锁

1、本地缓存

性能提升大神器:缓存,我们合理利用缓存就可以对系统性能有一个大提升

通过以前的性能优化,我们可以优化它的业务逻辑来增加它的吞吐量,但在更多的时候,对于一些复杂的业务,我们已经不可以通过优化它的业务逻辑来增加吞吐量了,比如一些复杂的查询功能,每次查询就会很麻烦,我们就需要使用缓存。第一次查询的时候就把它放入缓存里面,以后我们需要数据就直接从缓存里面获取,没必要进行复杂的查询,检索。

哪些数据适合放入缓存?

  • 即时性、数据一致性要求不高的

  • 访问量大且更新频率不高的数据(读多,写少)
    在这里插入图片描述
    最基本的缓存就是map(本地缓存)

  • 本地缓存问题:代码属于同一个进程的,在同一个jvm里面,如果是部署在单体应用使用本地缓存什么问题都没有(永远只部署在一台机器),
    本地缓存

分布式缓存

如果是在分布式系统下,商品服务会部署在十几台服务器下,每一个服务器都自带一个本地缓存,就会出现如下问题:

  • 第一次请求,负载均衡到第一个项目,查询缓存里面没有,查询一次数据库放入到缓存里面,如果下一次请求还是负载均衡到第一个项目就可以去缓存里面拿,如果是负载均衡第二个项目呢?我们还要去查询一次数据库放入到缓存里面,在下一次请求负载均衡到第三个项目呢?我们还要去查询一次数据库放入到缓存里面,由于这些缓存都是分开的,我们必须要各顾各的自己查一遍;

还有一个问题:

  • 如果我们对数据库进行修改,我们还要对缓存也进行一次修改,但是我们只修改第一个项目下的缓存,二号,三号项目没法改,因为我们负载均衡到的是第一个项目下的,那么以后我们去二号,三号里面得到的数据就会和一号不一样,所以这就产生了数据一致性问题,我们就不应使用本地缓存在分布式系统下

    image-20201230164745680

解决分布式缓存数据一致性问题

​ 我们可以把所有的服务产生的缓存都放到一个叫缓存中间件里面,大家都给集中的一个地方缓存数据,这样如果负载均衡到第一个服务器,缓存里面没有,就会查询数据库,并且放入到缓存中间件里面,以后就算负载均衡到第二个服务器,第一个服务器之前已经给缓存中间件放过了,我们第二个服务器只要直接从缓存中间件获取即可,无需在调用复杂的业务逻辑;同样如果数据发生修改,比如负载均衡到第三个服务器,除了修改数据库以外,也给缓存中间件进行更新,这样即使别的请求来到别的服务器,也是去操作同一个缓存中间件,我们还要去查询一次数据库放入到缓存里面

  • 最常用的缓存中间件:redis(可以做集群工作;分片存储,打破了本地缓存的容量限制;使用缓存中间件非常的方便;单独维护缓存中间件也可以做到高可用,高性能)

image-20201230164926295

  • 我们以后所有的数据开发都将把缓存放入到redis里面进行开发,不需要使用map(本地缓存)

高并发下缓存失效问题

1. 缓存穿透:

指查询一个一定不存在的数据,由于缓存是不命中,将去查询数据库,但是数据库也无此记录,我们没有将这次查询的null写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义

风险: 利用不存在的数据进行攻击,数据库瞬时压力增大,最终导致崩溃 缓存

解决: 我们可以给缓存放一个标志位,例如null结果加入缓存,并加入短暂过期时间

2. 缓存雪崩:

缓存雪崩是指在我们设置缓存时key采用了相同的过期时间, 导致缓存在某一时刻同时失效,请求全部转发到DB,DB瞬时 压力过重雪崩。

解决: 原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这 样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。

3.缓存击穿

  • 对于一些设置了过期时间的key,如果这些key可能会在某些时间点被超高并发地访问,是一种非常“热点”的数据。
  • 如果这个key在大量请求同时进来前正好失效,那么所有对这个key的数据查询都落到db,我们称为缓存击穿。

解决: 加锁

大量并发只让一个去查,其他人等待,查到以后释放锁,其他人获取到锁,先查缓存,就会有数据,不用去db

分布式锁

项目中整合redisson作为分布式锁等功能的框架:

采用看门狗机制:

默认的锁超时时间是30秒超时,但是Redisson提供了一个看门狗机制,锁的默认超时时间就是这个看门狗时间,默认30秒,可以通过config.lockWatchdogTimeout(10)来指定

看门狗机制流程是这样的:

  1. 线程获取分布式锁,锁的默认超时时间是30秒。
  2. 但是有可能业务代码比这个锁的时间要长,为了避免业务代码没执行完成锁释放了,看门狗机制默认会在看门狗时间的三分之一时对锁的时间进行重置,比如看门狗时间是30秒,业务代码执行50秒,那么看门狗机制就每个10秒对锁进行一次超时重置回30秒,直到业务代码执行完成释放锁为止。
  3. Redisson连接出现异常断开后,看门狗机制也就失效了,那么锁会在超时后释放,不再进行重置。

缓存数据一致性问题

双写模式

失效模式

解决方案

  • 无论是双写模式还是失效模式,都会导致缓存的不一致问题。即多个实例同时更新会出事。怎么办?
    • 1、如果是用户纬度数据(订单数据、用户数据),这种并发几率非常小,不用考虑这个问题,缓存数据加上过期时间,每隔一段时间触发读的主动更新即可
    • 2、如果是菜单,商品介绍等基础数据,也可以去使用canal订阅binlog的方式。
    • 3、缓存数据+过期时间也足够解决大部分业务对于缓存的要求。
    • 4、通过加锁保证并发读写,写写的时候按顺序排好队。读读无所谓。所以适合使用读写锁。(业务不关心 脏数据,允许临时脏数据可忽略);

总结:

  • 我们能放入缓存的数据本就不应该是实时性、一致性要求超高的。所以缓存数据的时候加上过期时间,保证每天拿到当前最新数据即可。
  • 我们不应该过度设计,增加系统的复杂性
  • 遇到实时性、一致性要求高的数据,就应该查数据库,即使慢点。

springcache简化缓存开发

  • 每次调用需要缓存功能的方法时,spring会检查检查指定参数的指定目标方法是否已经被调用过。如果有就直接从缓存中获取方法调用后的结果,如果没有就调用方法并缓存结果后返回给用户,下次调用直接从缓存中获取。

  • 其实和 spring 的事务管理类似,spring cache 的关键原理就是 spring AOP,通过 spring AOP,其实现了在方法调用前、调用后获取方法的入参和返回值,进而实现了缓存的逻辑。

更新时间:2020-12-30 17:22:56

本文由 阿俊 创作,如果您觉得本文不错,请随意赞赏
采用 知识共享署名4.0 国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
原文链接:https://jinterest.cn/archives/2-本地缓存分布式缓存数据一致性分布式锁md
最后更新:2020-12-30 17:22:56

评论

Your browser is out of date!

Update your browser to view this website correctly. Update my browser now

×