JB的测试之旅-缓存

1,407 阅读10分钟

前言

最近工作上遇到个问题,从用户A切换到用户B,结果用户B显示的还是用户A的数据

问题的原因很简单,用户B使用的还是用户A的缓存数据,解决方案也很简单,独立保存一份缓存即可;

但是,不由的想问,缓存测试这块,需要注意什么?

那先慢慢了解,到底什么是缓存?

什么是缓存

缓存是我们在生活中经常听到一个词,如怎么清理浏览器的缓存手机空间不够了,得删除缓存硬盘的缓存是不是越大越好等等的问题;

其实这些缓存可以分成3种:

  • 一种指硬件上的,像硬盘缓存和CPU缓存;
  • 一种客户端缓存;
  • 一种是指服务端缓存;

后两种更像是一种技术或者是服务;

  • 硬件缓存:指的是一块芯片,可以被集成到硬盘或者是CPU上。它的作用就是充当硬盘(CPU)与外界接口(通常是内存)之间的暂存器,利用缓存可以减轻系统的负荷,同时提高数据的传输速率;
  • 客户端缓存:某些应用,如浏览器,X宝,X信等,为了实现能够快速响应用户的请求,会把用户之前浏览的东西(如图片等)存在本地;在下次访问时,如果本地的缓存里有请求的内容,那么就直接展示出来,不用再次向服务器请求;
  • 服务端缓存:它与客户端缓存目的相同,只不过是站在服务器这边考虑的;如果每次接到客户端请求都要连接一次数据库,当用户请求多的时候,负载过大;这时可以把一些经常被请求的数据存放在内存中,当有请求时直接返回,不用经过数据库,这样就可以减轻数据库的负担;

小结

缓存是临时存放数据(使用频繁的数据)的地方,介于外部请求和真实数据之间;

为什么要用缓存

从一个用户的角度来看,体验最好的肯定是不管什么情况下,都能成功访问这个页面,并且打开的速度很快,也就是保证正常工作的前提下时间尽可能短;

如果没有缓存,我们的体验可能是这样的:

用户请求一个数据,这个数据得从数据库中去取,随着用户越来越多和数据量越来越大,每次用户请求的时间就会越来越长,而且数据库无时不刻都在工作;
这样用户和数据库都很痛苦,时间一长,就有可能发生下面两件事情:
1)用户很烦,抱怨页面加载太慢或者打不开,最后放弃了这个应用;
2)数据库满负荷工作,偶尔崩溃(导致页面错误);

分析原因,是由于数据库的连接数和连接时长是有限制的,但请求过多,超出了数据库能承受的范围,导致数据库崩溃;
那为什么不把一部分数据放在别的地方,这样有用户请求这些数据时,就不用从数据库中取了?

服务器缓存

工作原理

当客户端向服务器请求一个资源时,服务器首先在缓存中找,如果在缓存中,那么直接返回,不需要连接数据库;
如果请求的资源不在缓存中,这时再去数据库中找,找到后返回给客户端,并将这个资源加入缓存中;
这样下次请求相同资源时,就不需要连接数据库了。而且如果把缓存放在内存中,因为对内存的操作要比对数据库操作快得多,这样请求时间也会缩短;

所以,通过使用缓存,就可以保证满足用户的需求,在正常工作的前提下响应时间尽可能短;

特别需要注意缓存失效的场景,如:

  • 数据库的数据已经更新了,那对应的缓存数据也要更新;
  • 缓存超过失效时间,需要重新发起请求来更新缓存;
  • 缓存满了,比如缓存满了,重新发起请求,新的资源重新加入缓存,一般做法是把缓存里最旧的资源剔除,加入新的资源;

前端缓存

类型

缓存分为两类:强制缓存和协商缓存,定义如下:

  • 强制缓存:客户端直接从缓存中读取数据,不与服务器作交互;
  • 协商缓存:客户端发送特定的报文到服务器,服务器根据接收到的报文判断资源是否有更新,有则返回200和最新的资源文件,无则返回304使客户端从缓存中读取数据;

缓存由什么决定?

缓存由http报文的内容决定,关系如下:

image.png-96.7kB

max-age或者expires都决定了缓存的过期时间,会使客户端再次请求数据时先判断缓存是否过期,未过期则直接从缓存中读取数据(强制缓存);

两者的区别是前者是个相对值,相对于客户端的时间,后者直接定义了截止时间,且相对于服务端的时间

协商缓存由Last-ModifiedIf-Modified-SinceETagIf-None-Match两组报文决定;

字段的意思分别如下:

  • Last-Modified:表示服务器上某文件最近的修改时间,存在于响应报文;
  • If-Modified-Since:值等于Last-Modified,存在于请求报文,用于将Last-Modified值返回给服务端作比较;

缓存的总体过程

首次请求资源:

image.png-128.4kB

非首次请求资源:

image.png-411.5kB

在第一次请求资源后,浏览器会将资源连同响应报文一起缓存到本地,其中响应报文可能包含了关于缓存的头信息;

因而后续请求的时候,浏览器可以根据本地缓存的头信息知道资源的缓存决策,判断是否强制缓存,或者移交服务器判断是否协商缓存;

缓存穿透、缓存击穿、缓存雪崩

在服务器缓存里面,有3个特殊的名词:缓存穿透、缓存击穿、缓存雪崩

这3个到底是什么?

缓存穿透

正常情况下,查询的数据都存在,如果请求一个不存在的数据,也就是缓存和数据库都查不到这个数据,每次都会去数据库查询,这种查询不存在数据的现象称为缓存穿透

穿透带来的问题

如果每次都拿一个不存在的id去查询数据库,可能会导致你的数据库压力增大;

如何发现缓存穿透

  • 业务的响应时间:可以借助ELK或其他监控系统,对业务的接口进行检测,原本缓存就是响应时间比较快的,如果经常超过阈值就一定会有所体现;
  • 总调用数、cache层命中数、storage层命中数

解决办法--缓存空值

之所以发生穿透,是因为缓存中没有存储这些数据的key,从而每次都查询数据库;
可以为这些key在缓存中设置对应的值为null,后面查询这个key的时候就不用查询数据库了;
当然为了健壮性,我们要对这些key设置过期时间,以防止真的有数据;

缓存击穿

在高并发的情况下,大量的请求同时查询同一个key时,此时这个key正好失效了或者不存在,就会导致同一时间,这些请求都会去查询数据库,这样的现象称为缓存击穿

比如请求一些特殊字符,就会出现该情况;

引起的原因

  • 代码问题,比如保存到缓存时用的是固定值;
  • 恶意攻击,比如爬虫,在请求时传一些不正常的值;

带来的问题

会造成某一时刻数据库请求量过大;

解决办法

采用分布式锁,只有拿到锁的第一个线程去请求数据库,然后插入缓存,若其它线程获取锁失败,则等待一段时间后重试;

缓存雪崩

当某一时刻发生大规模的缓存失效的情况,比如缓存服务宕机了;

解决方法

  • 跟缓存穿透一样加锁排队;
  • 建立备份缓存,缓存A和缓存B,A设置超时时间,B不设置超时时间,先从A读缓存,A没有则读B,并且更新A和B的缓存;

解决热点数据集中失效问题

在设置缓存的时候,一般会给缓存设置一个失效时间,过了这个时间,缓存就失效了;

对于一些热点的数据来说,当缓存失效以后会存在大量的请求过来,然后打到数据库去,从而可能导致数据库崩溃的情况;

解决办法

  • 设置不同的失效时间;
  • 采用缓存击穿的解决办法,加锁;
  • 永不失效,就是采用定时任务对快要失效的缓存进行更新缓存和失效时间;

缓存的测试点

功能

  • 缓存是否可以正确被创建,包括位置、名字和内容;
  • 缓存是否被清除,包括主动清楚以及被第三方发起的清除,清除后是否正常工作及清除失效的情况;
  • 系统运行过程中,redis缓存数据生效、缓存的数据读取正确、数据写入落地正确,数据有效期设置合理;
  • 缓存与数据库的数据一致性检测;
  • DB事务性导致回滚,缓存是否回滚,有没有产生脏数据;
  • 缓存是否有大小限制,达到大小临界值如何处理;
  • 缓存时突然被中断,如何处理;
  • 缓存失效后,是否能正常表现;

自动化

  • 自动化用例中断言部分设计缓存层断言并且自动化框架本身对于断层层次可配置;

性能及稳定性

  • 关注业务本身应用场景及缓存结构,是否使用缓存;
  • 预防缓存穿透、缓存雪崩、缓存击穿引发的系统风险;

扩容

  • 关注扩容方案设计、老数据备份策略、回滚方案;
  • 关注扩容后分片策略的变化;
  • 扩容后热点数据失效率或命中率以及对后端DB带来的压力;

环境

无网络&有数据:

  • 缓存大小未超过,缓存时间有效期内,显示缓存数据加载;
  • 缓存大小超过,本地缓存数据删除,显示无网提示,无数据加载;
  • 缓存时间过期,本地缓存数据删除,显示无网提示,无数据加载;

无网络&无数据:

  • 显示无数据加载;

有网络&有数据:

  • 缓存大小未超过,缓存时间有校内,显示缓存数据加载;
  • 缓存大小超过了,本地缓存数据删除,直接从线上拉取数据;
  • 缓存时间过期,本地缓存数据删除,直接从线上拉取数据;

有网络&无数据:

  • 直接从线上拉取数据存到本地;

缓存存储

  • 客户端安装后,有网络,开始存储数据到本地;
  • 覆盖安装,缓存数据依然存储在本地;
  • 清除数据、卸载、重装,内存和本地缓存数据清零;

异常情况

  • 由于网络原因缓存失败,则无法读取缓存数据;
  • 由于服务器原因导致缓存失败,则无法取缓存数据;
  • 终端本地的数据接近满值,内存被占用,无法读取缓存数据;
  • 设置的缓存文件夹和数据文件不可读写;
  • 缓存的刷新机制是否手动操作;

小结

之前没深入了解过缓存,真的没有覆盖那么多场景,基本都需要了解技术细节,才能设计出这样的测试场景,偏后台内部逻辑的测试;