从程序员到架构师:大数据量、缓存、高并发、微服务、多团队协同等核心场景实战
上QQ阅读APP看书,第一时间看更新

4.3 缓存何时存储数据

使用缓存的逻辑如下。

1)先尝试从缓存中读取数据。

2)若缓存中没有数据或者数据过期,再从数据库中读取数据保存到缓存中。

3)最终把缓存数据返回给调用方。

这种逻辑唯一麻烦的地方是,当用户发来大量的并发请求时,它们会发现缓存中没有数据,那么所有请求会同时挤在第2)步,此时如果这些请求全部从数据库读取数据,就会让数据库崩溃。

数据库的崩溃可以分为3种情况。

1)单一数据过期或者不存在,这种情况称为缓存击穿

解决方案:第一个线程如果发现Key不存在,就先给Key加锁,再从数据库读取数据保存到缓存中,最后释放锁。如果其他线程正在读取同一个Key值,那么必须等到锁释放后才行。关于锁的问题前面已经讲过,此处不再赘述。

2)数据大面积过期或者Redis宕机,这种情况称为缓存雪崩

解决方案:设置缓存的过期时间为随机分布或设置永不过期即可。

3)一个恶意请求获取的Key不在数据库中,这种情况称为缓存穿透

比如正常的商品ID是从100000到1000000(10万到100万之间的数值),那么恶意请求就可能会故意请求2000000以上的数据。这种情况如果不做处理,恶意请求每次进来时,肯定会发现缓存中没有值,那么每次都会查询数据库,虽然最终也没在数据库中找到商品,但是无疑给数据库增加了负担。这里给出两种解决办法。

①在业务逻辑中直接校验,在数据库不被访问的前提下过滤掉不存在的Key。

②针对恶意请求的Key存放一个空值在缓存中,防止恶意请求骚扰数据库。

最后说一下缓存预热

上面这些逻辑都是在确保查询数据的请求已经过来后如何适当地处理,如果缓存数据找不到,再去数据库查询,最终是要占用服务器额外资源的。那么最理想的就是在用户请求过来之前把数据都缓存到Redis中。这就是缓存预热。

其具体做法就是在深夜无人访问或访问量小的时候,将预热的数据保存到缓存中,这样流量大的时候,用户查询就无须再从数据库读取数据了,将大大减小数据读取压力。

关于缓存何时存数据的问题就讨论完了,接下来开始讨论更新缓存的问题,这部分内容因涉及双写(缓存+数据库),所以会花费一些篇幅。