网站首页 > 技术文章 正文
你在互联网大厂做后端开发的时候,有没有被缓存穿透问题搞得焦头烂额呢?相信很多同行都有过这样的经历,原本以为缓存能大大提升系统性能,结果却因为缓存穿透,让数据库压力骤增,甚至影响到整个系统的稳定运行。今天,咱们就来好好唠唠这个缓存穿透问题,看看怎么把它彻底解决掉。
背景介绍
在互联网应用中,缓存是提升系统性能的重要手段。当用户发起请求时,系统首先会去缓存中查找数据,如果命中则直接返回,大大减少了数据库的压力。但缓存穿透现象却打破了这种理想状态。所谓缓存穿透,就是指查询一个根本不存在的数据,由于缓存中也没有该数据,所以每次请求都会直接穿透到数据库,当这类请求大量存在时,数据库就会不堪重负,甚至可能导致系统崩溃。比如在电商系统中,如果有人恶意利用不存在的商品 ID 进行大量查询,就可能引发缓存穿透问题。
解决方案
布隆过滤器(Bloom Filter)
这是一个非常有效的解决方案。在缓存层前增加布隆过滤器,它基于哈希算法,由一个位数组和多个哈希函数构成。所有合法 Key 经过多个哈希函数计算后,会在位数组的对应位置上标记为 1 。当有请求到来时,先通过布隆过滤器判断该 Key 是否合法,即将该 Key 经过相同的多个哈希函数计算,查看位数组对应位置是否都为 1,如果不是则直接返回,这样可以拦截大部分无效请求。举例来说,某电商平台在采用布隆过滤器后,成功拦截了 99% 的无效商品 ID 请求,数据库压力下降了 90%。
不过,布隆过滤器存在一定的误判率,我们可以通过调整哈希函数数量和位数组大小来优化。误判率计算公式为(( (1 - e^{-kn/m})^k )),其中(k)为哈希函数数量,(m)为位数组大小,(n)为元素数量 。实际应用中,需要权衡空间占用和误判率。例如,若元素数量(n = 1000000),希望误判率控制在 0.01%,经过计算,合适的(m)约为 1800 万位,(k)约为 12 个哈希函数。
同时,为了保证数据的实时性,需要定期同步数据库更新布隆过滤器,例如每天凌晨进行全量同步。也可以采用增量更新的方式,当数据库有新数据插入时,实时更新布隆过滤器。
缓存空对象(Null Caching)
当数据库未命中时,我们可以将空值缓存起来并设置一个短的 TTL(如 5 - 30 秒)。这样,后续相同的请求就可以直接从缓存中获取空值,避免再次访问数据库。以社交平台为例,对不存在的用户资料缓存空值后,减少了 80% 的穿透请求。
但要注意,空值需与正常数据区分开,比如可以在缓存值前加上特定的前缀标识,像 “NULL_”。另外,还可以通过异步清理空缓存,比如监听数据库 Binlog,当新增数据时删除对应的空缓存。或者设置一个定时任务,每隔一段时间扫描缓存,清理掉过期的空缓存,避免缓存中积累过多无用的空值,占用缓存空间。
业务层防御
在业务层对参数进行严格的过滤也很关键。比如 ID 必须为数字且长度固定等,防止非法参数进入系统。可以在代码中添加参数校验模块,例如在 Java 中使用正则表达式对传入的 ID 进行校验:
import java.util.regex.Pattern;
public class ParameterValidator {
private static final Pattern ID_PATTERN = Pattern.compile("^\\d{8}#34;);
public static boolean isValidId(String id) {
return ID_PATTERN.matcher(id).matches();
}
}
对同一 IP 的请求进行限流,例如每秒限制 10 次,避免恶意攻击。可以使用令牌桶算法或漏桶算法实现限流,以 Guava 库中的 RateLimiter(基于令牌桶算法)为例:
import com.google.common.util.concurrent.RateLimiter;
public class RequestLimiter {
private static final RateLimiter rateLimiter = RateLimiter.create(10);
public static boolean tryAcquire() {
return rateLimiter.tryAcquire();
}
}
对于敏感接口,强制要求用户登录验证,确保请求的合法性。可以通过 JWT(JSON Web Token)等技术实现用户身份验证,用户登录成功后生成 JWT 令牌,后续请求携带令牌,在业务层进行令牌验证,只有验证通过的请求才会继续处理。
总结
缓存穿透问题虽然棘手,但只要我们掌握了正确的方法,就能有效地解决它。大家不妨试试上述的解决方案,根据自己的业务场景进行优化。在实际应用过程中,如果遇到什么问题或者有更好的解决办法,欢迎在评论区留言分享。让我们一起努力,提升系统性能,为用户带来更流畅的体验。
猜你喜欢
- 2025-05-11 「linux」Socket缓存是如何影响TCP性能的?
- 2025-05-11 Ehcache:Java程序员的高性能缓存利器
- 2025-05-11 网站慢?试试这个Java实时缓存高招!
- 2025-05-11 Typecho 开启 Redis 缓存优化访问速度
- 2025-05-11 12 张图 | 硬刚了一波,三层缓存架构
- 2025-05-11 Spring Boot3 整合 Redis 实现数据缓存操作全解析
- 2025-05-11 6.14 在vSphere环境中配置主机交换缓存操作方法
- 2025-05-11 HTTP缓存如何提高Web应用程序的性能?
- 2025-05-11 AspNetCore 使用Redis实现分布式缓存
- 2025-05-11 如何正确清除 DNS 缓存吗?(解决你访问延时 )
你 发表评论:
欢迎- 05-11FANUC修改前端目录教程
- 05-11前端分享-Set你不知道的事
- 05-11jq+ajax+bootstrap改了一个动态分页的表格
- 05-11千万级大表分页查询效率剧降,你会怎么办?
- 05-11Elasticsearch深度分页
- 05-11如何写一个简单的分页
- 05-11手速太快引发分页翻车?前端竞态陷阱揭秘
- 05-11「linux」Socket缓存是如何影响TCP性能的?
- 最近发表
- 标签列表
-
- 前端设计模式 (75)
- 前端性能优化 (51)
- 前端模板 (66)
- 前端跨域 (52)
- 前端缓存 (63)
- 前端md5加密 (49)
- 前端路由 (55)
- 前端数组 (65)
- 前端定时器 (47)
- 前端懒加载 (45)
- 前端接口 (46)
- Oracle RAC (73)
- oracle恢复 (76)
- oracle 删除表 (48)
- oracle 用户名 (74)
- oracle 工具 (55)
- oracle 内存 (50)
- oracle 导出表 (57)
- oracle约束 (46)
- oracle 中文 (51)
- oracle链接 (47)
- oracle的函数 (57)
- mac oracle (47)
- 前端调试 (52)
- 前端登录页面 (48)
本文暂时没有评论,来添加一个吧(●'◡'●)