一、缓存更新策略
一般情况来说,缓存更新策略有三种:
- 先删除缓存,后更新数据库
- 先更新数据库,后更新缓存
- 先更新数据库,后删除缓存
- 更新数据库,监听数据库binlog
下面讨论在高并发情况下,三种策略的有缺点,以及可以改进的点
(一) 先删除缓存,后更新数据库
image.png有一个更新请求 A 和一个查询请求 B
1、 请求 A 发起更新请求,删除缓存
2、 请求 B 发起查询请求,缓存不存在
3、 请求 B 请求数据库获得旧数据
4、 请求 B 将旧数据回写到缓存
5、 请求 A 更新数据库
如果发生了上面的情况,就会出现数据库与缓存不一致的问题,解决方案:
- 设置缓存过期时间,那么会有短暂的数据不一致问题
- 更新数据库以后,删除缓存
(二) 更新数据库,同时更新缓存
image.png有两个更新请求A、B
1、 请求 A 更新数据库
2、 请求 B 更新数据量
3、 请求 B 更新缓存
4、 请求 A 更新缓存
如果出现上面的情况,缓存里面就是请求 A 更新的数据,数据库和缓存数据不一致
(三) 更新数据库、删除缓存
FaceBook采用该分案进行缓存的更新,在并发情况下,也有可能出现缓存不一致的情况
一个查询请求A,一个更新请求B:
1、 缓存刚好失效;
2、 请求A查询数据库,得一个旧值;
3、 请求B将新值写入数据库;
4、 请求B删除缓存;
5 、请求A将查到的旧值写入缓存
发生上述情况有一个先天性条件,就是步骤3的写数据库操作比步骤2的读数据库操作耗时更短,才有可能使得步骤4先于步骤5。可是,大家想想,数据库的读操作的速度远快于写操作的(不然做读写分离干嘛,做读写分离的意义就是因为读操作比较快,耗资源少),因此步骤3耗时比步骤2更短,这一情形很难出现。
如何避免上面的问题:
- 缓存设置有效期
- 异步延时删除缓存
(四) 更新数据库,监听数据库binlog
image.png1、更新商品数据
2、binlog 监听组件监听binlog变更
3、查询数据库数据,回写缓存
如果binlog监听组件出现问题,通过eventbus,或者mq通知缓存服务,更新缓存
二、缓存穿透
(一) 什么是缓存穿透?
1、缓存穿透是指在高并发清空下,如果某个key被高并发访问,并且在缓存中没有命中,出于容错的考虑,会从数据库中获取数据,然后对应的数据库中也没有该数据,导致在并发情况下数据库做了很多无用的查询操作,给数据库带来压力。
(二) 解决方案
1、 缓存NULL对象
如果无法从DB中查查询到对应的数据,那么在缓存中保存NULL对象,并设置失效期,防止后面数据库中添加了该条数据
2、预校验
如果知道数据的范围,先预判断一下查询的key是否在数据范围内,比如商品的主键是从 1 至 10000,如果查询的key是不在商品主键范围内,那么就直接返回
3、布隆过滤器
在访问缓存层和存储层之前,将存在的key用布隆过滤器提前保存起来
三、缓存雪崩
(一) 什么是缓存雪崩
1、缓存雪崩是指缓存数据不可用或者是大量缓存在同一时间段失效,导致大量的查询请求到DB,导致数据库压力增大甚至崩溃
(二) 解决方案
1、多级缓存,每一级缓存设置不同的超时时间,那么某个级别的缓存失效,也有其他级别缓存兜底
2、缓存实现时间设置随机值,比如某一批数据设置的超时时间都是10分钟,可以在10分钟的基础上加上一个随机数
3、分布式锁,如果某个商品是热点数据,如果数据在缓存中失效,可能会出现多个获取该商品的请求同时打到DB,为了防止这种情况,只有获取到锁的请求才能够请求到DB,其他请求阻塞
4、限流,对于打到DB的请求进行限流
四、缓存污染
(一) 什么是缓存污染
1、缓存污染主要出现在使用本地缓存的情况下,从本地缓存中获取数据以后,对数据进行修改,导致后面获取到的缓存数据都是污染以后的数据
(二) 解决方案
1、不允许修改从本地缓存中获取的数据对象
网友评论