• Geek_88604f
    2019-09-07
    对于一个确定的 x 值,如果 F(x) 永远不变,这就没问题。但如果 F(x) 值是不确定的,那就有可能会出现并发的两个 F(x) 请求得到的结果不同,从而导致缓存中的值和存储中的值不一致。
            这段描述我是这么理解的,老师的意思是不是两个线程同时去get同一个key,发现key不在缓存中,此时两个线程都会去计算key对应的value,当线程A拿到x完成计算后准备将计算结果刷到缓存(但还未刷新到缓存),线程B拿到了已经改变过的x(如果x表示select某个数据表的返回值,线程c有可能在线程B计算之前改变了x)也计算了值并先于线程A将值刷新到缓存,然后线程A才缓过劲来将它的计算结果刷新到缓存,这样导致的最终结果是缓存和存储中的值不一致。
            对于这种情况我觉得可以考虑以下几个方案去改进或规避:(1)考虑缓存的key为空的情况毕竟占少数,因此可以考虑当读取到缓存为空时随机等待几个毫秒的延迟后再次读取,如果key还为空则计算y=f(x)。(2)缓存更新序列化,将y=f(x)的计算从fastf(x)中移除,当缓存没有命中时向消息队列发起异步更新消息,消费者从消息队列中取消息计算f(x)并刷新缓存(如果短时间内有大量的更新消息,可以考虑只处理最新的消息),当然业务侧要做读取缓存的重试。(3)将y=f(x)的计算从fastf(x)中移除,设置缓存不老化,fastf(x)只负责读取数据,当缓存没有命中的时候从存储中读取。由x的变化发起者或者定时任务来计算f(x),当x发生变化的时候完成计算并刷新缓存。
    展开

    作者回复: 你的理解是对的,当存在并行的Set请求,自然存在时序问题,导致存储和缓存数据不一致。解决方案来说:
    1、sleep不能解决多任务协同问题,所以这个方案不可行。
    2、是可行的思路,把 F(x) 和 Set 一起串行执行。不过这会导致在缓存未命中时 F(x) 执行两遍。你说的缓存未命中从存储读,本质上是执行 F(x) 的意思。
    3、存储中 x 对应的数据发生变化时,我们通常的做法是把 x 从缓存中清除(Delete),而不是执行 F(x) 和 Set。原因是缓存空间是有限的,所以要给 Get 次数比较多的数据缓存,而不是一发生变更就缓存,这样非常可能反而降低了缓存命中率。

    
     8
  • Tachyon
    2019-09-20
    缓存雪崩并不是说缓存大量宕机,而是大量key几乎同时过期导致请求直接打到后端存储上。

    作者回复: 成因可以多样化,同时过期的效果和宕机显然类似。

    
     1
  • mickey
    2019-09-10
    祝许老师以及极客全体老师教师节快乐,工作顺利,身体健康!
    
     1
  • Aaron Cheung
    2019-09-09
    groupcache 学习了 打卡39
    
     1
  • Charles
    2019-09-08
    所以许老师怎么去评估一个系统是否应该上缓存,假设目前存储都可以顶住负载?谢谢

    作者回复: 要分析清楚压力和和效率瓶颈。压力大了,可以考虑加缓存,当然也可以考虑存储扩容。效率瓶颈,通常只能用缓存解决。

    
     1
  • 醉雪飘痕
    2019-09-07
    不好意思,突然明白了,应该是指缓存满进行的淘汰。而分不同的group后,各个group不相互影响,可独立进行淘汰,控制粒度更细。
    请忽略我上面的提问,谢谢。
    
     1
  • 醉雪飘痕
    2019-09-07
    “F(x)、G(x) 在同一个内存缓存集群就意味着它们相互之间会淘汰对方?”
    许老师,这里有些不理解,为何F(x)、G(x)会相互淘汰对方?
     2
     1
  • Dean
    2019-09-16
    如何理解groupcache值不可变就解决了一致性问题,此时如果存储的值变了,缓存中的相应的计算结果如果不变的话,不是也不一致么?

    作者回复: 你可以这么理解:以键值存储为例,它存储的是 key => (value, ver),而 groupcache 存储的是 (key, ver) => value。这样就可以做到存储的值改变而 groupcache 值不变。只不过怎么用,这个需要好好考虑一下。

    
    
  • JACK
    2019-09-10
    还是不太懂为什么groupcache解决了一致性问题,如果fx的实现易变,那fx,gx的组合也会变吧
    
    
  • Geek_88604f
    2019-09-06
    func FastF(x TypeX) (y TypeY) {
      key := toBytes(x)
      hash := hashOf(key)
      i := hash % countOf(memcaches)
      val, err := memcaches[i].Get(key)
      if err != nil {
        y = F(x)
        val = toBytes(y)
        memcaches[i].Set(key, val)
      } else {
        y = fromBytes(val)
      }
      return
    }
    这段代码第六行判断条件是=还是≠,没太明白整个逻辑。
    展开

    作者回复: err != nil 表示失败

    
    
  • leslie
    2019-09-06
    课程讲到现在算是明白为何redis、memcache的考虑因素了:之前很多地方的讲解没有涉及到底层算法,故而让我们觉得好像类似,但是实际上完全不同的;原来是忽略了底层算法。
          老师今天的课从不一样的角度去解释:彻底明白为何差不多的的东西其实各种称呼方式,大多数情况下其实很多时候没有强调 缓存与内存,这个概念被统称了。谢谢老师的分享。
    
    
  • 诗泽
    2019-09-06
    如果 F(x) 值是不确定的,这种情况下放缓存里也就没意义了吧?

    作者回复: 这里说的不确定不是太准确,正确应该说会改变,有多个版本

    
    
  • CoderLim
    2019-09-06
    缓存热数据可以有效缓解存储的压力,提高响应速度,但是设计时需要考虑扩缩容是否影响 hash 映射,是否重试友好,是否有持久性的需求
    
    
  • 风清扬
    2019-09-06
    许老师,缓存血崩的原因是命中率降低,大量请求直达后端,后端性能极速下降导致,解决办法是抛弃过多的请求。想到的一个是应用层既网关层限流。能详细讲解下吗?

    作者回复: 后面还会细聊这些问题

    
    
我们在线,来聊聊吧