缓存由http报文的内容决定,关系如下:
max-age或者 expires都决定了缓存的过期时间,会使客户端再次请求数据时先判断缓存是否过期,未过期则直接从缓存中读取数据(强制缓存);
两者的区别是前者是个相对值,相对于客户端的时间,后者直接定义了截止时间,且相对于服务端的时间;
协商缓存由 Last-Modified、 If-Modified-Since 或 ETag、 If-None-Match两组报文决定;
字段的意思分别如下:
- Last-Modified:表示服务器上某文件最近的修改时间,存在于响应报文;
- If-Modified-Since:值等于 Last-Modified,存在于请求报文,用于将 Last-Modified值返回给服务端作比较;
缓存的总体过程
首次请求资源:
非首次请求资源:
在第一次请求资源后,浏览器会将资源连同响应报文一起缓存到本地,其中响应报文可能包含了关于缓存的头信息;
因而后续请求的时候,浏览器可以根据本地缓存的头信息知道资源的缓存决策,判断是否强制缓存,或者移交服务器判断是否协商缓存;
缓存穿透、缓存击穿、缓存雪崩
在服务器缓存里面,有3个特殊的名词:缓存穿透、缓存击穿、缓存雪崩;
这3个到底是什么?
缓存穿透
正常情况下,查询的数据都存在,如果请求一个不存在的数据,也就是缓存和数据库都查不到这个数据,每次都会去数据库查询,这种查询不存在数据的现象称为缓存穿透;
穿透带来的问题
如果每次都拿一个不存在的id去查询数据库,可能会导致你的数据库压力增大;
如何发现缓存穿透
- 业务的响应时间:可以借助ELK或其他监控系统,对业务的接口进行检测,原本缓存就是响应时间比较快的,如果经常超过阈值就一定会有所体现;
- 总调用数、cache层命中数、storage层命中数;
解决办法--缓存空值
之所以发生穿透,是因为缓存中没有存储这些数据的key,从而每次都查询数据库;
可以为这些key在缓存中设置对应的值为null,后面查询这个key的时候就不用查询数据库了;
当然为了健壮性,我们要对这些key设置过期时间,以防止真的有数据;
缓存击穿
在高并发的情况下,大量的请求同时查询同一个key时,此时这个key正好失效了或者不存在,就会导致同一时间,这些请求都会去查询数据库,这样的现象称为缓存击穿;
比如请求一些特殊字符,就会出现该情况;
引起的原因
- 代码问题,比如保存到缓存时用的是固定值;
- 恶意攻击,比如爬虫,在请求时传一些不正常的值;
带来的问题
会造成某一时刻数据库请求量过大;
解决办法
采用分布式锁,只有拿到锁的第一个线程去请求数据库,然后插入缓存,若其它线程获取锁失败,则等待一段时间后重试;
缓存雪崩
当某一时刻发生大规模的缓存失效的情况,比如缓存服务宕机了;
解决方法
跟缓存穿透一样加锁排队;
建立备份缓存,缓存A和缓存B,A设置超时时间,B不设置超时时间,先从A读缓存,A没有则读B,并且更新A和B的缓存;