[论文阅读]FlightTracker-Facebook跨网在线存储读一致性优化(1)

169 阅读13分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第32天,点击查看活动详情

[论文阅读]FlightTracker-Facebook跨网在线存储读一致性优化(1)

第43篇,终于到论文正文,请注意,Facebook扩展了RYW定义,也就是有所放宽:

以用户为中心的会话: 我们希望实现以用户为中心的会话RYW保证,这意味着我们在几个层面上经历了会话内的并发,如图1所示:一个单一的网络请求并行地发出TAO读和写;一个浏览器或移动应用程序同时有许多网络再查询;一个用户甚至可能从多个设备同时访问Facebook。 论文见:www.usenix.org/conference/…

1. 简介

社交媒体平台提供新鲜和定制的内容聚合。这种特征组合使得提前聚合变得不那么容易;相反,Facebook的每个应用级网络请求都可能向我们的图存储TAO[20]发出数百或数千次查询,以呈现单一的响应。这种高查询放大意味着数据存储的读取必须高效、低延迟和高可用性。在Facebook,我们已经用一个异步耦合的缓存、数据库副本和定制索引的联盟来解决这一挑战,将社交媒体数据和元数据建模为一个图。虽然这个读优化的生态系统实现了高性能,但要为开发者提供一个直观和统一的一致性模型,这是个挑战。

image-20211105113013086

FlightTracker是我们管理RYW一致性的解决方案,用于在线访问Facebook的社交图。它在提供RYW一致性的同时,保留了最终一致性的读取效率、热点容忍度和高可用性。FlightTracker提供了一个统一的终端用户会话的概念,它跨越了许多有状态的服务,并且可以扩展到新的数据存储,而无需改变架构。

FlightTracker由一系列的API和一个元数据服务组成。在写集跟踪技术[28, 40, 41, 51]和CRDTs[18, 50]的基础上,FlightTracker服务对用户最近的写集的元数据进行统计,并将这些元数据作为一种数据类型公开,我们称之为Ticket。一旦用户被识别,网络请求就会获取用户的Ticket(见图1)。在所有后续的网络请求对社交图的查询中,都会自动附加这个Ticket。

我们使用各种系统特定的策略来确保每一个由 Ticket 识别的写作都能反映在查询结果中。例如,我们对缓存的策略是忽略那些与Ticket中的写相比可能已经过时的缓存条目;我们将由此产生的缓存缺失称为一致性缺失。当系统在处理查询时需要从另一个组件获取数据时,可以递归地传播Ticket。

FlightTracker自2016年以来一直在生产。它为数十亿用户和每天10的15次方数据存储查询提供RYW一致性。对于Facebook的大多数内部应用程序和开发人员来说,FlightTracker是自动和隐藏的。一些调用网站和高层基础设施组件明确地操作Ticket以加强一致性水平。FlightTracker的松散耦合设计使我们能够逐步推出对两个缓存系统、三个索引系统和两个数据库的支持。它预先提供了这些数据存储在最终一致性下的效率、延迟和可用性。

总的来说,本文有五个方面的贡献。

• 我们总结了Facebook在TAO(一个读取优化的跨地区复制图库)中依赖RYW的写通缓存时所遇到的挑战(第2节)。

• 我们提出了Ticket抽象,它以可扩展的方式跨服务边界封装了写集的系统特定细节(§ 4)。

• 我们展示了我们如何在FlightTracker服务中存储和交换Ticket,以提供RYW一致性(第3节和第5节)或明确满足特定用例的替代一致性要求(第7节),同时容忍热点(第6.5节)。

• 我们解释了我们用于在查询服务系统中实现包含Ticket的读取的各种策略(第6节),其中包括用于简单缓存和具有复杂更新管道的全局索引的策略(第6.3节)。

• 我们在生产环境中评估了FlightTracker,证明它保留了底层读优化存储的有用属性(§ 8),并分享了一些经验教训(§ 8.5)。

2. 动机

TAO是一个读取优化的数据存储,提供对Facebook的社交图的访问[20]。它是在一个跨地区复制的数据库前面使用两层缓存来实现的。TAO最初依靠写过的缓存来实现一致性。这种技术在最终一致性的基础上提供了RYW,同时保留了系统的读取效率和热点容忍度,因为它允许大多数查询从附近的L1缓存服务器提供。

随着Facebook的发展,我们发现我们需要一个更好的方法来实现一致性。TAO最初的写通过策略依赖于使用固定的通信模式:用户被负载均衡器粘在一个单一的L1缓存集群上,集群间的通信被限制在穿越一个固定的树上,并且写是沿着读miss时将遵循的相同的树穿越链进行代理的。如果以下情况发生,RYW将被违反:用户请求被路由到另一个网络服务器集群;网络服务器集群到L1高速缓存集群的映射被改变;查询被失败到一个陈旧的副本;高速缓存内容在异步复制发生之前被丢失;或任何查询是由TAO以外的数据存储提供。 下图是我从Tao论文找到的一个读流程。 image-20211105143442444

2.1 横向扩展带来的挑战

随着TAO规模的扩大,我们发现依靠固定的通信拓扑结构的问题越来越多。事实上,随着时间的推移,写通RYW所需的每一个条件都变得难以满足。随着跨集群网络的改进,我们放弃了将L1缓存与Web服务器集群配对和搭配的做法,减少了每个区域的L1缓存

副本的数量。这减少了数据的缓存副本的数量,但它需要将网络服务器的流量部分或动态分配给L1缓存集群以获得合理的平衡。从集群粘性到区域粘性的用户路由改善了网络服务器集群和TAO的负载分配。随着地理区域数量的增加,我们开始在一些没有本地数据库副本的数据中心去使用TAO,将缓存失误路由到最近的邻近区域。如果我们被限制在一个树状拓扑结构中进行缺失路由和缓存失效流,一个数据库副本的中断会影响到多个地区。图2显示了我们遇到的一些理想的通信模式,这些模式打破了写-通过一致性模型。虚线和虚线箭头显示了在没有FlightTracker的情况下有可能违反RYW一致性的读取请求。

另一个反复出现的问题是需要跨集群或全局写入的查询。TAO将这些查询标记为关键性的,将它们路由到持有数据库primary DB的区域中的L2缓存,靠近通信树的底部[20]。这种策略有延迟和可用性方面的缺点。它也不能容突发流量导致的负载尖峰的工作负载。

2.2 跨系统的一致性

随着我们遇到了挑战,扩大TAO的写入方法的一致性,社交图的生态系统扩大。应用程序开发人员从直接访问TAO转向使用一种查询语言,这种语言可以很容易地表达图上的多跳和属性过滤谓词。这一层间接性使我们能够建立额外的系统,为Facebook查询工作量的一个子集量身定做。

一些应用查询在映射到TAO的简单API上时涉及到许多往返,并且传输了大量的数据,客户端会立即丢弃。全局二级索引可以优化这些查询的通信模式,但只有当索引存储具有与TAO相同的语义时,使用索引透明地或追溯性地优化执行才是安全的[34]。我们的索引系统是松散耦合的,通过异步流水线更新,重新洗牌、转换和过滤。松散的耦合使开发和部署分开,但它限制了一致性的实施策略。大多数索引的分片方式与TAO不同,所以即使我们使用更多的单片设计,它们也不能同步参与写入路径而不降低可用性和增加尾部延迟[17, 54]。

迁移到社交图的应用级查询语言的另一个副作用是,使用替代的数据库技术作为社交图的部分记录系统变得更加容易,例如对于那些经历了高写入率或有限寿命的数据类型。这些系统也经历了与TAO一样的一致性挑战。

Ajoux等人[10]先前确定了在Facebook的社会媒体平台上提供因果一致性的四个基本挑战:在许多有状态的服务中进行整合,容忍高查询放大,处理关键对象(即热点),并为用户提供净收益。我们的经验是,这些挑战在提供RYW一致性时也会出现,最困难的障碍是产生一个能同时解决所有问题的设计。

2.3 为什么需要read-your-writes?

数据存储的一致性模型保证了哪些写入的内容对读取是可见的。应用开发人员使用这些保证来推理整个系统的正确性。像线性化[32]或因果一致性[9]这样的强势模型通常为开发者提供更简单的经验和心理模型,但它们限制了实现。

为读优化系统提供一致性保证,可以归结为实现一种滞后性检查,以确定缓存或副本是否可以用其本地数据为读查询服务。这种滞后性检查必须是。(1)本地的,在大多数情况下避免网络通信;(2)高度细化的,所以很少有查询会因为检查的假阳性而导致额外的工作;(3)有利于增量修复,所以寻找新鲜数据的额外工作可以被重新用于次序查询。重要的是,即使在使用同步quorum写入的系统中,单次复制的读取也需要滞后性检查。例如,Raft追随者[43]或Paxos接受者[38]如果不属于提交quorum,就可能不知道领导者所承诺的写入。

逻辑和物理时间戳,如Hybrid逻辑时钟[36]、Spanner的TrueTime[26]和Occult的com- pressed vector clocks[41],提供了一种简单和可扩展的方法来检查滞后性--如果本地数据是足够新鲜的,那么它的时间戳高于所需的读取时间戳。不幸的是,这些方法既不细化也不利于增量修复。例如,如果本地存储比所需的读取时间戳晚了10秒,它就不能为任何查询提供服务,直到它处理完所有丢失的写入。

对于我们的工作负载来说,重要的是我们可以在本地为大多数查询提供服务,即使本地副本有几秒钟的陈旧。这导致我们拒绝接受这样的一致性水平,即从陈旧副本中丢失的所有写(线性化)或大多数写(因果一致性)都需要可见。相比之下,RYW允许我们通过添加有限的写("你的 "写)的新鲜版本来利用一个陈旧的副本。我们也拒绝了像有界滞后性这样的较弱的模型,它不能保证用户看到他们自己的写,这对一个交互式的应用来说很难正确使用[54]。1

我们在Facebook的经验是,简单的RYW一致性模型[51]对于应用开发者和我们的终端用户来说是一个合理的默认模型,并有一个扩展:我们希望将会话的概念扩展到终端用户。

image.png

下图就是Facebook做的RYW扩展,这里甚至是一个用户的两个终端的RYW image.png

以用户为中心的会话: 我们希望实现以用户为中心的会话RYW保证,这意味着我们在几个层面上经历了会话内的并发,如图1所示:一个单一的网络请求并行地发出TAO读和写;一个浏览器或移动应用程序同时有许多网络再查询;一个用户甚至可能从多个设备同时访问Facebook。

RYW会话保证的原始定义[51]意味着会话内的读和写是完全有序的,并且这种会话内的顺序与这些操作的物理顺序相吻合,如线性化。因此,虽然理论上的定义不需要单线程的会话,但实现上将会话限制在一个客户端、写入器、会话管理器或服务器[19, 28, 42, 52]。

然而,我们的观察是,应用程序开发人员并不期望并发的网络请求能够相互通信。一个常见的心理模型是,并发请求以随机的顺序执行,并且可能相互交错,所以只有当一个请求在另一个请求开始之前完成时,才能保证从一个请求到另一个请求的可见性。应用程序开发人员的直觉是,数据被保证可用的第一个时刻是写确认的时候能由发出网络请求的本地程序接收。

这一观察结果促使我们对RYW的保证做了如下放宽,这也是FlightTracker所提供的。

对社交图的读取将观察到同一终端用户在先前完成的网络请求中或在同一网络请求中所做的所有写操作。

这个定义为我们提供了处理会话内并发问题所需要的灵活性。请注意,在最初的RYW定义中,读可以通过返回一个更新的数据版本来 "观察 "一个写。