什么是缓存穿透
缓存穿透是指在使用缓存系统的情况下,恶意或者恶意的查询请求,频繁地访问不存在于缓存中的数据。
如下图所示:
由于恶意攻击,频繁地访问不存在于缓存中的数据,从而导致缓存系统无法有效地减轻后端数据库的压力,甚至可能导致数据库被频繁地查询。
缓存穿透产生原因
缓存穿透通常是由恶意查询、查询频率高、数据变化快或随机键查询等原因引起的。
主要分为如下4大原因:
1.恶意查询
攻击者或者恶意用户故意请求不存在于缓存中的数据,以消耗系统资源或观察系统行为。
2.查询频率高
某些数据很少被访问,因此不被缓存,但如果某个时刻有大量请求查询这些数据,就会导致缓存穿透。
3.数据变化快
如果数据源中的数据频繁变动,而缓存设置的过期时间较长,会导致缓存中的数据过期,需要重新从数据源获取。
4.随机键查询
一些系统的查询键是随机生成的,这可能导致查询缓存中不存在的数据。
缓存穿透解决方法
1.缓存空值或错误标记
在缓存中存储空值或错误标记,当查询请求发现缓存中的数据为这些标记时,可以避免对数据源的不必要查询。
如下所示:
import java.util.Map; import java.util.concurrent.ConcurrentHashMap; public class CacheManager { private Map<String, String> cache = new ConcurrentHashMap<>(); public String getUserInfo(String userId) { String cachedData = cache.get(userId); if (cachedData != null) { if (cachedData.equals("null")) { // 返回空值,避免重复查询 return null; } else { return cachedData; } } // 数据不在缓存中,查询数据库 String userData = queryDatabase(userId); if (userData == null) { // 用户不存在,将空值存入缓存,过期时间设置较短 cache.put(userId, "null"); } else { // 用户数据存入缓存 cache.put(userId, userData); } return userData; } private String queryDatabase(String userId) { // 模拟数据库查询 // 在实际应用中,这里应该查询数据库或者其他数据源 return null; // 返回null表示用户不存在 } }
通过这种方式,可以有效地阻止频繁查询不存在的数据。
2.布隆过滤器
使用布隆过滤器来快速检查一个查询是否可能存在于缓存中,从而减少不必要的查询操作。
布隆过滤器是一种数据结构,用于快速检测某个元素是否属于一个集合,可以有效降低缓存穿透问题的发生。
布隆过滤器是1970年由布隆提出的一种数据结构,它由一个很长的二进制向量(位向量、位数组、Bit Array)和一系列的随机映射函数组成。
如下图所示:
主要通过3个步骤来实现:
- 我们通过三个Hash函数分别将元素a、b、c存储到上图的位数组中,将九个位置为1;
- 当检索某个元素时,如果三个哈希函数运算后所得到的位值都为1,那么布隆过滤器会告诉我们元素存在;
- 如果有一个位值为0,那么布隆过滤器会告诉我们元素不存在。
3.合理设置缓存过期时间
确保缓存中的数据不会长时间过期,以减少缓存穿透问题的发生。
可以根据数据的访问模式和需求来设置不同的缓存过期时间。
例如:对热门数据设置短期过期时间,对冷门数据设置较长的过期时间。
4.热点数据预加载
将热门数据提前加载到缓存中,以降低数据库负载,这可以通过定期更新缓存中的热点数据或者使用缓存预热技术来实现。
通过综合使用这些方法,可以有效地解决缓存穿透问题,提高系统性能和可用性,同时降低数据库的负载。
mikechen
mikechen睿哥,10年+大厂架构经验,资深技术专家,就职于阿里巴巴、淘宝、百度等一线互联网大厂。
关注「mikechen」公众号,获取更多技术干货!

后台回复【架构】即可获取《阿里架构师进阶专题全部合集》,后台回复【面试】即可获取《史上最全阿里Java面试题总结》