加入收藏 | 设为首页 | 会员中心 | 我要投稿 怀化站长网 (https://www.0745zz.cn/)- 语音技术、云资源管理、物联设备、云计算、决策智能!
当前位置: 首页 > 云计算 > 正文

坐拥最大的高速缓存,Facebook 如何保证缓存一致性?

发布时间:2022-08-02 11:22:10 所属栏目:云计算 来源:互联网
导读:缓存有助于减少延迟,提高重读工作负载的可扩展性,并且节省成本。实际上缓存是无处不在的,它也在你的手机和你的浏览器中运行。例如,CDN和DNS本质上是地理复制的缓存。正是由于许多缓存在幕后工作,你现在才能阅读这篇文章。 Phil Karlton有句名言:计算机
  缓存有助于减少延迟,提高重读工作负载的可扩展性,并且节省成本。实际上缓存是无处不在的,它也在你的手机和你的浏览器中运行。例如,CDN和DNS本质上是地理复制的缓存。正是由于许多缓存在幕后工作,你现在才能阅读这篇文章。
  Phil Karlton有句名言:“计算机科学中只有两个难题:缓存失效和命名。”如果你曾经处理过的无效缓存,那么你很有可能遇到过缓存不一致这个恼人的问题。
 
  在Meta,我们运营着世界上最大的高速缓存,包括TAO和Memcache。多年来,我们将TAO的缓存一致性提高了一个档次,从99.9999%(六个九)提高到99.99999999%(十个九)。
 
  当涉及到缓存无效时,我们相信我们现在有一个有效的解决方案来弥补理论和实践之间的差距。这篇博文中的原则和方法广泛适用于大多数(如果不是所有)的缓存服务。无论你是在Redis中缓存Postgres数据,还是将分散数据具象化,都是如此。
 
  我们希望能帮助减少工程师必须处理的缓存失效问题,并帮助增强缓存的一致性。
 
  一、定义缓存失效和缓存一致性
  根据定义,缓存并不是你数据的真实来源(例如数据库)。缓存失效描述的是当真实源中的数据发生变化时,主动将陈旧的缓存条目失效的过程。如果缓存失效处理不当,就会在缓存中无限期地保留一个不一致的值。
 
  缓存失效涉及到一个必须由缓存自身以外的程序来执行的动作。一些程序(例如,客户端或公共/子系统)需要告诉缓存其中数据发生了变化。仅仅依靠TTL来保持有效性的缓存,不在本文讨论范围之内。在这篇文章的其余部分,我们将假设存在缓存失效操作。
 
  为什么这个看似简单的过程在计算机科学中被认为是个困难的问题?下面是个简单的例子,说明如何引入缓存不一致的问题。
 
  
 
  缓存首先尝试从数据库中填充x。但是在 "x=42 "到达缓存主机之前,有人将x设置为43。缓存失效事件 "x=43 "首先到达缓存主机,将x设置为43。"x=42 "到达了缓存,将x设置为42。现在数据库中"x=43 ",而缓存中 "x=42 "。
 
  有很多方法来解决这个问题,其中之一就是维护版本字段。这样我们就可解决冲突,因为旧的数据不应该覆盖新的数据。但是,如果缓存条目 "x=43 @version=2 "在 "x=42 "到达之前就失效了呢?在这种情况下,缓存数据依然是错误的。
 
  缓存失效的挑战不仅来自于失效协议的复杂性,还来自于监控缓存一致性和如何确定缓存不一致的原因。设计一个一致的缓存与操作一个一致的缓存有很大不同,就像设计Paxos协议与构建在生产中实际运行的Paxos一样,都有很大区别。
 
  二、我们为什么要关心缓存的一致性
  我们必须解决复杂的缓存失效问题吗?在某些情况下,缓存的不一致性几乎和数据库数据丢失一样严重。从用户的角度来看,它甚至和数据丢失没有区别。
 
  让我们来看看另一个关于缓存不一致如何导致脑裂的例子。Meta公司使用消息将其从用户在主存储数据的映射到TAO中。它经常进行移动,以保证用户可以就近访问。每次你向某人发送消息时,系统都会查询TAO,以找到消息的存储位置。许多年前,当TAO的一致性较差时,一些TAO副本在重新移动后会出现不一致的数据,如下例所示。
 
  想象一下,在将Alice的主消息存储从区域2切换到区域1后,Bob和Mary,都向Alice发送了消息。当Bob向Alice发送消息时,系统查询了靠近Bob居住地的区域的TAO副本,并将消息发送到区域1。当Mary向Alice发送消息时,系统查询了靠近Mary居住地的地区的TAO副本,命中了不一致的TAO副本,并将消息发送到了地区2。Bob和Mary将他们的消息发送到不同的区域,而两个区域都没有爱丽丝消息的完整副本。
 
  
 
  三、缓存失效模型
  
 
  了解缓存失效的困难之处尤其具有挑战性。让我们从一个简单的模型开始。缓存的核心是一个有状态的服务,它将数据存储在一个可寻址的存储介质中。分布式系统本质上是一种状态机。如果每个状态转换都能正确执行,我们就会有一个按预期工作的分布式系统。否则,系统就会问题。所以,关键的问题是:对于有状态的服务,什么在改数据?
 
  
 
  静态缓存有一个非常简单的缓存模型(例如,简化的CDN接近这个模型)。数据是不可改变的。没有缓存主动失效。对于数据库来说,数据只有在写入(或复制)时才会发生变化。我们通常对数据库的每一个状态变化都有日志。每当发生异常时,日志可以帮助我们了解发生了什么,缩小问题的范围,并找出问题所在。构建容错的分布式数据库(这已经很困难了),有其独特的挑战。这些只是简化的模型。
 
  
 
  对于像TAO和Memcache这样的动态缓存,数据在读取(缓存填充)和写入(缓存失效)的路径上都会发生变化。这种组合使得多竞态条件成为可能,而缓存失效则是一个困难的问题。缓存中的数据是不持久的,这意味着有时候对解决冲突很重要的版本信息会被清除出去。结合所有这些特点,动态缓存产生的竞态条件超出了我们的想象。
 
  而且,记录和跟踪每一个缓存状态的变化几乎是不现实的。缓存经常被引入来扩展重读的工作负载。这意味着大部分的缓存状态变化都来自缓存填充路径。以TAO为例。它每天提供超过四亿次的查询。即使缓存命中率达到99%,我们每天也要进行超过10万亿次的缓存填充。记录和追踪所有的缓存状态变化会使一个重读的缓存工作负载变成一个极重写的日志系统工作负载。调试一个分布式系统已经带来了巨大的挑战。调试一个没有缓存状态变化的日志或追踪的分布式系统,基本是不可能的。
 
  尽管有这些挑战,我们还是提高了TAO的缓存一致性,这些年来从99.9999%提高到99.99999999%。在文章的其余部分,我们将解释我们是如何做到的,并强调一些未来的工作。
 
  四、针对一致性的可观察性
  为了解决缓存失效和缓存一致性问题,第一步涉及测量。我们要测量高速缓存的一致性,并在高速缓存中出现不一致的条目时发出警报。测量不能包含任何假阳性。人类的大脑可以很容易地调出噪音。如果存在任何误报,人们很快就会学会忽略它,而这个测量也变得毫无用处。我们还需要测量是精确的,因为我们谈论的是测量超过10个九的一致性。如果一个修正已经落地,我们要保证我们可以定量地测量它带来的改进。
 
  
 
  为了解决测量问题,我们建立了一个名为Polaris的服务。对于一个有状态的服务中的任何异常,只有当客户能够以这种或那种方式观察到它,它才是一个异常。否则,它就根本不重要。基于这一原则,Polaris 专注于测量违反客户可观察不变量的情况。
 
  在高层次上,Polaris作为客户端与有状态的服务进行交互,并且不假设了解服务内部。这使得它是通用的。Meta有几十个服务使用Polaris。"缓存最终应该与数据库一致 "是Polaris监控的一个典型的客户端可观察到的不变因素,特别是在异步缓存失效的情况下。在这种情况下,Polaris假装是一个缓存服务器并接收缓存失效事件。例如,如果Polaris收到一个无效事件,说 "x=4 @version 4",它就会作为客户查询所有的缓存副本,以验证是否有任何违反该不变性的情况发生。如果一个缓存副本返回 "x=3 @version 3",Polaris将其标记为不一致,并重新等待样本,以便以后针对同一目标缓存主机进行检查。Polaris在某些时间尺度上报告不一致,例如一分钟、五分钟或十分钟。如果这个样本在一分钟后仍然显示为不一致,Polaris就将其报告为相应时间尺度的不一致。

(编辑:怀化站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    热点阅读