【专项测试系列】-缓存击穿、穿透、雪崩专项测试

数据库 创建于:2023-05-29

作者:刘须华

一、背景概述: R2M缓存的使用,极大的提升了应用程序的性能和效率,特别是数据查询方面。而缓存最常见的问题是缓存穿透、击穿和雪崩,在高并发下这三种情况都会有大量请求落到数据库,导致数据库资源占满,引起数据库故障。平时对缓存测试时除了关注增删修改查询等基本功能,应该要重点关注缓存穿透、击穿和雪崩三种异常场景的测试覆盖,避免出现线上事故。

二、基本概念说明

1、缓存击穿:是指在超级热点数据突然过期,导致针对超级热点的数据请求在过期期间直接打到数据库,这样数据库服务器会因为某一超热数据导致压力过大而崩掉。



2、缓存穿透:是指查找的数据在缓存和数据库中都不存在,导致每一次请求数据从缓存中都获取不到,而将请求打到数据库服务器,但数据库中也没有对应的数据,最后每一次请求都到数据库;如果在高并发场景或有人恶意攻击,就会导致后台数据库服务器压力增大,最终系统可能崩掉。



 

3、缓存雪崩:是指突然缓存层不可用,导致大量请求直接打到数据库,最终由于数据库压力过大可能导致系统崩掉。缓存层不可用指以下两方面:缓存服务器宕机,系统将请求打到数据库; 缓存数据突然大范围集中过期失效,导致大量请求打到数据库重新加载数据, 与缓存击穿的区别在于这里针对很多key缓存,前者则是某一个key。



三、测试工具(非必须)

1、使用Titan压测平台进行并发请求测试

2、使用jmeter工具模拟并发请求

四、测试方法举例说明(非必须)

环境:测试环境

工具:jmeter

(1)缓存穿透场景

测试方法:查询一个根本不存在的数据,缓存层和存储层都不会命中。

查询接口相关代码实现:



通过JMETER模拟多次重复调用:单线程重复调用



查看日志结果: 从日志可以看出:执行并发请求后, 所有请求每次都走向了数据库。



预防方案:

当数据库查询为空时,将缓存赋值默认值,后续查询都走缓存,减少数据库压力。

上述接口,增加赋值为empty,则第一次查询到数据库为空,后续查询都查询到缓存中,缓存值为empty。



再次执行并发测试:从日志可以看出,可以看出每个ID都只执行了一次数据库查询并设置缓存,之后请求都命中了缓存,有效防止了缓存穿透问题。



(2)缓存击穿场景

测试方法:对某个Key有大量的并发请求,这时从缓存中删除这个key。模拟热key过期失效的场景。这个时候大并发的请求可能会瞬间把后端DB压垮。

接口相关部分代码实现:



操作步骤:

1、查询pin为liuxuhua的请求,这时pin为liuxuhua的数据会加载到缓存

2、再次查询pin为liuxuhua的请求,命中缓存

3、50并发请求pin为liuxuhua的数据,这个时候请求全部命中缓存

4、将pin为liuxuhua的缓存手动删除,模拟缓存失效

5、50并发请求pin为liuxuhua的数据,这个时候大量请求走向数据库,pin为liuxuhua的缓存被击穿

 



查看日志结果:



预防方案:

在设置默认缓存值的基础上,进行加锁处理。只有拿到锁的第一个线程去请求数据库,然后插入缓存,当然每次拿到锁的时候都要去查询一下缓存有没有。



从日志记录可以看到只有一个请求执行了数据库查询并设置缓存,其他请求都命中了缓存, 有效防止了缓存的击穿。



(3)缓存雪崩

测试方法:对多个使用到缓存的接口进行并发调用,设置这些缓存时间已过期(即删除缓存),调用时这些接口查询缓存时无数据,去查询数据库,这些请求都指向数据库,数据库压力增大,耗时增加。

模拟接口:





通过JMETER模拟多次重复调用:单线程多接口重复调用



查看日志结果:可以看出大量请求到达数据库,并且同一个pin或id执行了多次数据库查询



预防方案:

增加限流操作,即接口频繁调用时,增加一个缓存,设置时间为3s,3s内处理一定次数的请求,超过限制次数的请求直接返回结果,不做处理。

接口:3s内处理6次请求,超过则不处理;



从日志可以看出:可以看到每个都只查询了一次数据库并设置缓存,之后的请求都命中了缓存



五、测试指标:(或者叫通过标准,包括关注点以及意义)

1、模拟缓存穿透场景测试,每个不存在的数据都只执行了一次数据库查询并设置缓存,之后请求都命中了缓存,有效防止了缓存穿透问题。

2、模拟缓存雪崩场景测试,每个缓存失效的数据都只执行了一次数据库查询并设置缓存,之后请求都命中了缓存。

3、模拟缓存击穿场景测试,缓存失效的那个数据只有一个请求执行了数据库查询并设置缓存,其他请求都命中了缓存。

六、适用业务场景:

1、秒杀活动

2、热门营销活动

3、618和双11大促

七、研发侧常见解决方案(参考):

1、缓存穿透解决方案:

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

2、BloomFilter BloomFilter 类似于一个hbase set 用来判断某个元素(key)是否存在于某个集合中 我们把有数据的key都放到BloomFilter中,每次查询的时候都先去BloomFilter判断,如果没有就直接返回null 注意BloomFilter没有删除操作,对于删除的key,查询就会经过BloomFilter然后查询缓存再查询数据库,所以BloomFilter可以结合缓存空值用,对于删除的key,可以在缓存中缓存null 缓存击穿

2、缓存击穿解决方案:

采用分布式锁,只有拿到锁的第一个线程去请求数据库,然后插入缓存,当然每次拿到锁的时候都要去查询一下缓存有没有

3、缓存雪崩解决方案:

1、采用集群,降低服务宕机的概率

2、ehcache本地缓存 + 限流&降级

3、均匀过期,通常可以为有效期增加随机值

原文地址:https://my.oschina.net/u/4090830/blog/5595015

免责声明:本文来源于互联网,版权归合法拥有者所有,如有侵权请公众号联系管理员

* 本站提供的一些文章、资料是供学习研究之用,如用于商业用途,请购买正版。

京东云开发者