缓存穿透导致接口被恶意刷爆,该怎么防?

小庆玲 阅读 23

最近上线了一个商品详情页,发现有些不存在的商品 ID 被频繁请求,直接打穿缓存压垮了数据库。我试过在 Vue 里加了个 loading 防重,但根本没用,因为人家是直接调接口的。

现在每次遇到无效 ID,后端还是会去查数据库然后返回空,感觉这就是典型的缓存穿透。有没有前端能配合做的防护?比如对无效 ID 也缓存一段时间?

<template>
  <div v-if="product">{{ product.name }}</div>
  <div v-else>加载中...</div>
</template>

<script>
export default {
  async mounted() {
    this.product = await fetch(/api/product/${this.$route.params.id}).then(r => r.json());
  }
}
</script>
我来解答 赞 3 收藏
二维码
手机扫码查看
1 条解答
端木爱琴
前端能做的其实很有限,主要还得靠后端来解决缓存穿透的问题。不过这里给你个后端的解决方案,直接用布隆过滤器加缓存空值的方式。

首先在后端加上布隆过滤器,对请求的商品ID先做个快速校验:
from bloom_filter import BloomFilter

bloom = BloomFilter(max_elements=10000, error_rate=0.1)
# 初始化时把数据库中所有商品ID加入布隆过滤器
for product_id in get_all_product_ids():
bloom.add(product_id)

def check_product_id(product_id):
if product_id not in bloom:
return False
return True


然后在后端接口里这样处理:
cache = {}

def get_product(product_id):
if product_id not in cache:
if not check_product_id(product_id):
# 对于不存在的商品ID也缓存个5分钟
cache[product_id] = (None, time.time() + 300)
return None

result = query_db(product_id)
if not result:
# 缓存空结果1分钟
cache[product_id] = (None, time.time() + 60)
else:
cache[product_id] = (result, time.time() + 3600) # 正常数据缓存1小时

product_data, expire_time = cache[product_id]
if time.time() > expire_time:
del cache[product_id]
return get_product(product_id)

return product_data


前端这边还是老老实实调用接口吧,别自己搞太复杂了。这个方案虽然不能完全避免恶意请求,但至少能保护数据库。唉,又要加班优化了...
点赞
2026-03-26 03:03