美文网首页
缓存问题记录

缓存问题记录

作者: Jokerone_ | 来源:发表于2017-05-07 14:00 被阅读0次

    缓存击穿问题

    概念:缓存在某个时间点过期的时候,恰好在这个时间点对这个Key有大量的并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。
    解决:业界比较常用的做法,是使用mutex。简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db,而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一个mutex key,当操作返回成功时,再进行load db的操作并回设缓存;否则,就重试整个get缓存的方法。类似下面的代码。

    public String get(key) {
          String value = redis.get(key);
          if (value == null) { //代表缓存值过期
              //设置3min的超时,防止del操作失败的时候,下次缓存过期一直不能load db
              if (redis.setnx(key_mutex, 1, 3 * 60) == 1) {  //代表设置成功
                   value = db.get(key);
                          redis.set(key, value, expire_secs);
                          redis.del(key_mutex);
                  } else {  //这个时候代表同时候的其他线程已经load db并回设到缓存了,这时候重试获取缓存值即可
                          sleep(50);
                          get(key);  //重试
                  }
              } else {
                  return value;      
              }
      }
    

    缓存并发问题

    一张优惠券引发的血案(缓存并发问题)

    概念:数据在redis中以list方式存储。当某个时间点,缓存不存在,请求量又很大的时候,相同的数据有可能重复push到list中,导致数据重复。
    解决:使用分布式锁+双重检锁。
    注意:使用hash可以解决数据重复问题,但是并未解决缓存重复更新的问题。
    1.查询缓存,如果缓存存在,返回结果
    2.缓存不存在,查询数据库
    3.争夺分布式锁
    4.成功获得锁,再次判断缓存的存在
    5.如果缓存仍旧不存在,把查询数据库的结果循环放入缓存
    6.释放分布式锁
    伪代码如下:

    Paste_Image.png

    但是这样还是存在一个问题,当for循环没有push完全的时候,其他线程已经可以看到redis中的key,这样拿到的数据应该是不完整的数据。
    可以类比单例模式下的volatile的使用。
    解决方案:从缓存中load数据的时候,也必须拿到“分布式锁key”,使得数据完整安全。

    相关文章

      网友评论

          本文标题:缓存问题记录

          本文链接:https://www.haomeiwen.com/subject/vvmutxtx.html