文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

从SpringBoot构建十万博文聊聊缓存穿透,并发量过大该如何抗压?

2024-12-24 20:23

关注

在博客系统中,为了提升响应速度,加入了 Redis 缓存,把文章主键 ID 作为 key 值去缓存查询,如果不存在对应的 value,就去数据库中查找 。这个时候,如果请求的并发量很大,就会对后端的数据库服务造成很大的压力。

[[313074]]

造成原因

案例分析

由于文章的地址是这样子的:

https://blog.52itstyle.top/49.html

大家很容易猜出,是不是还有 50、51、52 甚至是十万+?如果是正儿八经的爬虫,可能会读取你的总页数。但是有些不正经的爬虫或者人,还真以为你有十万+博文,然后就写了这么一个脚本。

  1. for num in range(1,1000000): 
  2.  //爬死你,开100个线程 

解决方案

设置布隆过滤器,预先将所有文章的主键 ID 哈希到一个足够大的 BitMap 中,每次请求都会经过 BitMap 的拦截,如果 Key 不存在,直接返回异常。这样就避免了对 Redis 缓存以及底层数据库的查询压力。

这里我们使用谷歌开源的第三方工具类来实现:

  1.  
  2.  com.google.guava 
  3.  guava 
  4.  25.1-jre 
  5.  

编写布隆过滤器:

  1.  
  2. @Component 
  3. public class BloomCacheFilter { 
  4.  public static BloomFilter<Integer> bloomFilter = null
  5.  @Autowired 
  6.  private DynamicQuery dynamicQuery; 
  7.   
  8.  @PostConstruct 
  9.  public void init(){ 
  10.  String nativeSql = "SELECT id FROM blog"
  11.  List list = dynamicQuery.query(nativeSql,new Object[]{}); 
  12.  bloomFilter = BloomFilter.create(Funnels.integerFunnel(), list.size()); 
  13.  list.forEach(blog ->bloomFilter.put(Integer.parseInt(blog.toString()))); 
  14.  } 
  15.   
  16.  public static boolean mightContain(long key){ 
  17.  return bloomFilter.mightContain((int)key); 
  18.  } 
  19. 然后,每一次查询之前做一次 Key 值校验:

    1.  
    2. @RequestMapping("{id}.shtml"
    3. public String page(@PathVariable("id") Long id, ModelMap model) { 
    4.  if(BloomCacheFilter.mightContain(id)){ 
    5.  Blog blog = blogService.getById(id); 
    6.  model.addAttribute("blog",blog); 
    7.  return "article"
    8.  }else
    9.  return "error"
    10.  } 

    效率

    那么,在数据量很大的情况下,效率如何呢?我们来做个实验,以 100W 为基数。

    1. public static void main(String[] args) { 
    2.  int capacity = 1000000; 
    3.  int key = 6666; 
    4.  BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), capacity); 
    5.  for (int i = 0; i < capacity; i++) { 
    6.  bloomFilter.put(i); 
    7.  } 
    8.   
    9.  long start = System.nanoTime(); 
    10.  if (bloomFilter.mightContain(key)) { 
    11.  System.out.println("成功过滤到" + key); 
    12.  } 
    13.  long end = System.nanoTime(); 
    14.  System.out.println("布隆过滤器消耗时间:" + (end - start)); 

    布隆过滤器消耗时间:281299,约等于 0.28 毫秒,匹配速度是不是很快?

    错判率

    万事万物都有所均衡,既然效率如此之高,肯定其它方面定有所牺牲,通过测试我们发现,过滤器有 3% 的错判率,也就是说,本来没有的文章,有可能通过校验被访问到,然后报错!

    1. public static void main(String[] args) { 
    2.  int capacity = 1000000; 
    3.  BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), capacity); 
    4.  for (int i = 0; i < capacity; i++) { 
    5.  bloomFilter.put(i); 
    6.  } 
    7.  int sum = 0; 
    8.  for (int i = capacity + 20000; i < capacity + 30000; i++) { 
    9.  if (bloomFilter.mightContain(i)) { 
    10.  sum ++; 
    11.  } 
    12.  } 
    13.  //0.03 
    14.  DecimalFormat df=new DecimalFormat("0.00");//设置保留位数 
    15.  System.out.println("错判率为:" + df.format((float)sum/10000)); 

    通过源码阅读,发现 3% 的错判率是系统写死的。

    1. public static  BloomFilter create(Funnel funnel, long expectedInsertions) { 
    2.  return create(funnel, expectedInsertions, 0.03D); 

    当然我们也可以通过传参,降低错判率。测试了一下,查询速度稍微有一丢丢降低,但也只是零点几毫秒级的而已。

    1. BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), capacity,0.01); 

    那么如何做到零错判率呢?答案是不可能的,布隆过滤器,错判率必须大于零。为了保证文章 100% 的访问率,正常情况下,我们可以关闭布隆校验,只有才突发情况下开启。比如,可以通过阿里的动态参数配置 Nacos 实现。

    1. @NacosValue(value = "${bloomCache:false}", autoRefreshed = true
    2. private boolean bloomCache; 
    3. //省略部分代码 
    4. if(bloomCache||BloomCacheFilter.mightContain(id)){ 
    5.  Blog blog = blogService.getById(id); 
    6.  model.addAttribute("blog",blog); 
    7.  return "article"
    8. }else
    9.  return "error"

    小结

    缓存穿透大多数情况下都是恶意攻击导致的空命中率。虽然十万博客还没有被百度收录,每天也就寥寥的几十个IP,但是梦想还是有的,万一实现了呢?所以,还是要做好准备的!

     

    免责声明:

    ① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

    ② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

    软考中级精品资料免费领

    • 历年真题答案解析
    • 备考技巧名师总结
    • 高频考点精准押题
    • 资料下载
    • 历年真题
    • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

      难度     813人已做
      查看
    • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

      难度     354人已做
      查看
    • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

      难度     318人已做
      查看
    • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

      难度     435人已做
      查看
    • 2024年上半年系统架构设计师考试综合知识真题

      难度     224人已做
      查看

    相关文章

    发现更多好内容
    咦!没有更多了?去看看其它编程学习网 内容吧