缓存问题
缓存一致性的解决方案
实时同步:
增量,主动,强一致性。
- 对数据库进行更新的时候(增删改)淘汰缓存(缓存失效)
- 读取数据的时候去更新缓存,为了避免缓存击穿带来的雪崩问题,我们需要做同步处理,控制只有一个线程去读取数据,然后更新缓存,其他线程阻塞等待。
- 设置缓存失效时间,兜底操作,假设在更新缓存失败这个缓存失效时间一道,就会把缓存失效。
读取的时候,先去查看缓存是否存在,再去看看数据库里面的对不对
更新的时候,先删除缓存,再更新数据
准实时同步:
增量,被动,准一致性
- 对数据更新操作的时候在更新数据库后,发送一个更新缓存的MQ消息(如果要保证数据不丢失的话,建议可以建立本地一个消息表,在发送MQ失败的时候可以重试)
- 缓存更新服务消费MQ更新数据消息后,读取数据库,进行相应的业务处理。
- 更新服务更新业务处理结果数据到缓存中。
任务调度更新
- 分布式调度任务,进行定时的更新缓存,使用场景:报表统计数据,对账数据定时更新到缓存等实时性不高的场景
- 实现简单:
- Timer或者ScheduledExecutorService
- SpringTask定时任务。@Scheduled(cron=“0 0 0 /1 * * ?”)
- 定时任务框架Quartz
binlog日志订阅
- binlog来更新缓存,消费服务作为mysql的一个slave,订阅binlog,解析出更新内容,再更新到缓存。
- 优点:模拟mysql的slave主从,性能比定时任务要好,数据的一致性可以得到保证。mysql压力不大的时候延时也比较低。
- 缺点:要单独搭建一个同步服务,
缓存穿透
- 利用不存在的key频繁攻击应用,就会导致应用挂掉
- 针对业务场景,对请求的参数进行有效性校验,防止非法请求击垮db,比如用户id<=0的直接拦截
- 如果db查询不到数据,保存空对象到缓存层,设置较短的失效时间。
- 采用布隆过滤器来保存缓存过的key,在访问请求到来的时候,可以过滤掉不存在的key,防止这些请求到达db层。
缓存击穿
- 缓存当中没有数据,但是数据库当中有数据(一般是缓存时间到期了),并发用户比较多,同时缓存又没有读到数据,就一起去数据库当中读数据,引起数据库压力瞬间增大,造成过大压力。
- 设置热点数据永不过期。如果缓存数据不设置失效时间的话,就不会存在热点key过期,导致大量请求到达数据库
- 加互斥锁:当缓存失效的时候,保证一个请求能访问到数据库,并且更新缓存,其他线程等待并且重试
缓存雪崩
- 大批数据到达过期时间,查询数量巨大,引起数据库压力过大,甚至down机。
- 缓存击穿是指查同一个数据,缓存雪崩是好多数据都过期了,很多数据都查不到,从而查数据库。
- 缓存过期时间设置随机,防止同一时间大量数据过期现象发生。
- 如果缓存数据库是分布式数据库,将热点数据均匀分布在不同的缓存服务器当中
- 设置热点数据永不过期