缓存相关问题
缓存穿透
定义
查询一个不存在的数据,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到DB去查询,可能导致DB挂掉
解决方法
-
布隆过滤器
将数据库中所有的查询条件,放入布隆过滤器中,当一个请求过来时,先经过布隆过滤器进行查询,如果判断请求查询值存在,则继续查,如果判断请求查询不存在,直接丢弃
-
缓存空对象
查询返回的数据为空,仍把这个空值进行缓存,并设置一个较短的过期时间
布隆过滤器(Bloom Filter)
- 概念:是一种空间效率高的概率型数据结构,专门用来检测集合中是否存在特定的元素
- BF是由一个长度为m比特的位数组与k个哈希函数组成的数据结构。位数组均初始化为0,所有哈希函数都可以分别把输入数据尽量均匀的散列
- 当要查询是否存在一个元素的时,将其数据输入哈希函数,然后检查对应的k个比特。如果有任意比特为0,表明该数据一定不在集合中。如果所有比特都为1,则该数据大概率在集合中。
- 一个比特被置为1可能受其他元素的影响,所以说是大概率
优点:空间效率和查询时间都远超过一般的算法
缺点:具有一定的误识别率和删除困难
缓存击穿
定义
对于设置了过期时间的key,缓存在某个时间点过期的时候,恰好对这个key有大量的并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并设回到缓存,这时候大量并发的请求可能会瞬间把DB压垮
解决方法
- 使用互斥锁:当缓存失效时,不立即去load db,先使用redis的setnx去设置一个互斥锁,当操作成功返回时再进行load db的操作并设回缓存,否则重试get缓存方法
- 设置热点数据永不过期
- 实时更进,如果发现一个数据快过期,并且最近这个数据访问特别多,后台新启一个异步线程,重新在缓存层中添加这个数据的缓存
缓存雪崩
定义
设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部打到db,db瞬间压力过重雪崩
解决方法
- 设置热点数据永不过期
- 设置随机过期时间,避免缓存在同一时间大批失效
其他雪崩情况
在项目刚发布的时候,数据库中充满了数据,缓存是空的。如果此时的系统上线,请求又多,那就是造成雪崩
解决方法
-
灰度发布
通过灰度发布进行缓存预热,一流量小,数据库压力小。缓存也可以多出很多数据,渐渐增加流量,缓存也不为空,就可以解决
缓存一致性
定义
缓存的数据不是最新数据,而是老数据
处理缓存的策略
- Cache Aside模式(新数据来了,更新数据库——删除缓存)
- Read/Write Through模式(新数据来了,更新数据库——更新缓存)
不一致场景
-
有新数据来了,先删缓存,再更新数据库
缓存删除成功了,数据可更新失败。缓存失效,会访问数据库。如果此时有一个用户执行读操作,就会将数据库中的旧值读入缓存中,导致缓存最终存的是旧数据
-
有新数据来了,先更新数据库,再删除缓存
数据更新成功,缓存删除失败。这样缓存最终存储的也是老数据
解决方法
- Cache Aside模式
- 设置过期时间。对于所有数据,都设置一个过期时间,这样一来,肯定能保证数据库的最终一致性
- 延迟双删
- 先删除缓存
- 再把新数据写入缓存
- 休眠一段时间
- 再删除数据库
- Read/Write Through模式
- 消息队列。当更新数据时,数据库完成更新后,给消息队列发一下更新信息。同时,设置一个服务,用来订阅这个消息队列,如果消息队列中有新记录,就将新数据更新到redis缓存,如果缓存更新失败,就重试
- 利用Bin log。模拟Mysql的主从复制
主从复制
在Mysql多机主从复制模式下,主库如果有新数据进行更新,就会把更新的记录利用bin log的方式发给从节点,从节点收到binlog后,根据binlog进行处理,使得从库数据也变成新数据