社交网络场景下大规模图存储实践——Facebook TAO

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 社交网络场景下大规模图存储实践——Facebook TAO

概述

Facebook TAO[1] ,即 The Associations and Objects 的缩写,点(对象,Object)和边(联结,Associations)是”图“中最基本的抽象,用来做 Facebook 图存储名字倒是恰如其分。

概括来说,TAO 是 Facebook 为了解决社交场景下,超大数据的更新与关联读取问题,其核心特点如下:

  1. 提供面向 Facebook 社交信息流场景特化的图 API ,比如点查、一度关联查询、按时间的范围查询。
  2. 两层架构,MySQL 做存储层,MemeCache 做缓存层;缓存层又可细分为主从两层。
  3. 可多机房扩展,高度面向读性能优化,只提供最终一致性保证。

历史沿革

Facebook 早期沉淀的数据就在 MySQL 上[2],MySQL 扛不住后,在 2005 年时,扎克伯格便引入了 MemCache 做缓存层,应对更高频的读请求。自此之后,MySQL 和 MemCache 便成为了 Facebook 存储层技术栈的一部分。

Facebook 数据请求负载通常符合时间局部性(即最近更新的数据最容易被访问),而非空间局部性。但 MySQL 中的数据通常不是按照时间有序存储的,因此 MySQL InnoDB 引擎自带的 block cache 并不匹配这一特点。另外,MemCache 本身只提供基于内存的 KV 访问模型,为了更高效的利用这些内存,Facebook 需要针对社交场景自己定制缓存策略,以尽可能多的让读请求命中。

将这些工程细节,包括两层存储集群,包括自行组织缓存,都暴露给应用层工程师,带来了很大的工程复杂度,引发了更多的 bug,降低了产品迭代速率。为了解决这个问题,Facebook 在 2007 年使用 PHP 在服务端做了一个抽象层,基于图存储模型,围绕点(对象)和边(联结)提供 API。由于社交场景中的喜欢、事件、页面等都可以通过图模型来方便表达,这一抽象层极大的降低了应用层工程师的心智负担。

但随着所需 API 越来越多,将图模型层(在 webserver 上)和数据层(在 MySQL和MemCache 集群)分离实现的缺点逐渐暴露了出来:

  1. 从边集合的微小更新,会导致整个边集合失效,从而降低缓存命中率。
  2. 请求边列表的一个微小子集也需要将整个边列表从存储端拉到服务端。
  3. 缓存一致性很难维持。
  4. 当时的 MemCache 集群很难协同支持实现一个纯客户端侧的惊群避免策略。

所有这些问题,都可以通过重新设计统一的、基于图模型的存储层来实现。从 2009 年开始,TAO 便在 Facebook 内部的一个团队开始酝酿。再之后,TAO 逐渐发展成了支撑每秒数十亿次读取、数百万次写入,部署于跨地区海量机器上的分布式服务。

图模型 & API

图的最基本组成就是点和边,对应到 TAO 里就是,对象(Objects)和联结(Associations)。对象和联结都可以包含一系列由键值对表示的属性。

Object: (id) → (otype, (key  value)*)
Assoc.: (id1, atype, id2) → (time, (key  value)*)

:TAO 中的边都是有向边。

以社交网络为例,对象可以是用户、打卡、地点、评论,联结可以是朋友关系、发表评论、进行打卡、打卡于某地等等。

如下图 a),假设在 Facebook 上有这么一事件:Alice 和 Bob 在金门大桥打了个卡,Cathy 评论:真希望我也在那。David 喜欢了这条评论

用图模型表示后,如下图 b):

image.png

                                               一个栗子

可以看到,所有的数据条目如用户、地点、打卡、评论都被表示成了带类型的对象(typed objec),而对象间的关系如被谁喜欢(LIKED_BY)、是谁的朋友(FRIEND)、被谁评论(COMMENT),则被表示成了带类型的联结(typed associations)。

另外,尽管 TAO 中联结都是单向的,但实际中大部分关系是双向的。这时,可以增加一个反向边(inverse edges)来表示此种双向关系。

最后,由于联结是三元组,因此两个对象间可以有多条不同类型的边,但是同一类型的边,只能有一条。但在有些非社交场景中,可能需要相同类型的边也有多条。

Object API

围绕 Object 的操作,是常见的增删改查(create / delete / set-fields / get  )。

同一对象类型(object type)的对象具有同样的属性集(fields,即上面提到的 (key value)*),也就是说,一个对象类型对应固定的属性集。可以通过修改对象类型的 Schema 来对其所含属性进行增删。

Association API

围绕 Association 的基本操作,也是增删改查。其中增删改如下:

assoc_add(id1, atype, id2, time, (k→v)*) – 新增或者覆盖
assoc_delete(id1, atype, id2) – 删除
assoc_change_type(id1, atype, id2, newtype) - 修改

值得一说的是,如果其反向边((id1, inv(atype), id2))存在,则上述 API 会同时作用于其反向边。由于多数场景下的联结是双向的,因此 Facebook 将其边的 API 默认行为同时作用于两条边。

另外,每个 Association 都会自动打上一个重要的特殊属性:联结时间(association time)。由于 Facebook 负载具有时间局部性,利用此时间戳可以对缓存数据集进行优化,以提高缓存命中率。

Association Query API

围绕 Association 的查询 API,是 TAO 的核心 API,流量最大。这负载类型包括:

  1. 指定 (id1, type, id2) 的点查,通常用来确定两个对象间是否存在对应联结,或者获取对应联结的属性。
  2. 指定 (id1, type) 的范围查询,要求结果集按时间降序排列。比如一个常见场景:该条内容最新的 50 条评论是什么?。此外,最好能提供迭代器形式的访问。
  3. 指定 (id1, type) 出边数查询。比如查询*某条评论的喜欢数是多少?*此种查询很常见,因此最好将其直接存下来,以能够在常数时间内返回结果。

尽管联结千千万,但最近的范围是重点查询对象(时间局部性),因此联结的查询 API 主要围绕时间的范围查询展开。

为此,TAO 将最基本的联结集定义为 Association List。一个 Association List 是以 id1 为起点,出边类型为 atype 的所有联结的集合,按时间降序排列。

Association List: (id1, atyle) -> [a_new, ..., a_old]

基于此,定义更细粒度的几个接口:

// 返回以 id1 为起点,以 id2set 集合所包含点为终点
// 创建时间 time 满足 low <= time <= high
// 的联结集合。
assoc_get(id1, atype, id2set, high?, low?) 
// 返回联结集合的数量
assoc_count(id1, atype)
// 返回下标满足 [pos, pos+limit) 的联结集合子集
// pos 即 Association List 中的下标
assoc_range(id1, atype, pos, limit) 
// 返回创建时间 time 满足,从 time <= high **倒序**起始,
// 到 time >= low 终止,不超过 limit 条联结
assoc_time_range(id1, atype, high, low, limit)

为什么结果集按时间降序排列呢?因为在 Facebook 页面信息流展示时,总是先展示最新的,然后随着不断下拉,依次加载较旧的数据。

举个栗子:

 “50 most recent comments on Alice’s checkin” ⇒ assoc_range(632, COMMENT, 0, 50)
• “How many checkins at the GG Bridge?” ⇒ assoc_count(534, CHECKIN)

架构

image.png

                                           TAO 架构

TAO 架构整体分两层,缓存层(caching layer)和存储层(storage layer)。

存储层

由于前面所说的历史原因,TAO 使用 MySQL 作为存储层。

因此,TAO 对外的 API 最终会被转化成 MySQL 语句作用于存储层,但对 MySQL 的查询语句都相对简单。当然,存储层也可以使用 LevelDB 这种 NoSQL 存储引擎,这样查询语句就会对应翻译为前缀遍历。当然,选择存储引擎不止要看 API 翻译方便与否,还要看数据备份、批量导入导出、多副本同步等非 API 因素。

单个 MySQL 服务肯定存不下所有 TAO 数据,因此 TAO 使用了 MySQL 集群支撑存储层。为了将数据均匀的分到多个 MySQL 机器上,TAO 使用一致性哈希算法将数据在逻辑上进行了切片(shard)。每个切片存到一个 MySQL db 中。每个 Object 在创建时会关联一个 shard,并将 shard_id 做到 object_id 中,因此在 Object 整个生命周期中其 shard 都不会再改变。

具体来说,MySQL 中所存数据主要包括两张表,一个点表,一个是边表。其中,点和其出边会存在同一个 MySQL db 中,以最小化关联查询代价。所有的点属性在保存时,会被序列化到一个叫做 data 的列。如此,可以将具有不同类型的 Object 保存到一张表中。边和点保存时类似,但是会额外在 id1,atype,andtime  字段上做索引,以方便基于某个点的出边的范围查询。此外,为了避免对边的数量的查询所带来的高昂开销,会额外用一张表来保存 associations 的数量。

缓存层

读写穿透。TAO 的存储层实现了所有对外 API,对客户端( Client )完全屏蔽了存储层。即,Clients 只和缓存层进行交互,缓存层负责将数据同步到存储层。缓存层也是由多个缓存服务器构成,能够 Serve 任意 TAO 请求的一组缓存服务器称为一个 Tier。单个请求会路由到单个缓存服务器,不会跨多个服务器。

缓存策略使用经典的 LRU。值得一提的是,由于 TAO 的边默认是双向的,在 Client 写入边时,由缓存层变成负责将其变为写去边和回边的两个有向边,但 TAO 并不保证其原子性。失败了会通过垃圾回收来删除中间结果。

两层架构。TAO 中的每个逻辑分片(Shard)基本是同构的。每个逻辑分片的缓存层包括一组缓存服务器,由单个 Leader 缓存服务器和一组 Follower 缓存服务器构成。

其中,Followers 缓存服务器是外层,Leader 服务器是内层。所有客户端只和 Followers 打交道,Followers 缓存服务器本身只负责读请求,如果发现读未命中或者有写请求,就将其转发给所对应  Leader 缓存服务器。

如果读请求负载持续增加,对 Follower 缓存服务器扩容即可。

如果对某些 object 访问显著高于其他,TAO 会通过记录访问频次对其识别,然后进行客户端侧的缓存,并通过版本号来维持一致性。

一致性。Leader 收到多个 Follower 的并行写请求后会将其进行定序,序列化后到存储层进行同步读写后返回;对于写请求来说,还会异步的通知其他 Follower 服务进行对应数据的更新,因此 TAO 最终只能提供最终一致性保证。这样做的好处是换来了读请求的高吞吐。

多地扩展。由于 TAO 的读请求频次约为写频次的 25 倍,而单地数据中心(datacenter)又不能满足 Facebook 全球场景。因此 TAO 整体上使用了主从架构,两个 datacenter 都部署一套存储层+缓存层作为主从(Primary-Secondary),所有写请求都要由从数据中心的 Leader Cache 路由到主数据中心(见上图),然后由主数据中心存储层异步传回从数据中心。但从数据中心的 Leader Cache 并不等本地存储层同步回数据,即进行更新,并通知 Followers 到自己这 Refill。TAO 的这种设计,能够最大化的保证一个读取请求在一个 DataCenter 内被满足,代价是客户端可能会读到过时数据。即牺牲一致性,来降请求低延迟,提高吞吐。

一致性

TAO 在一致性和可用性取舍方面时,选择了后者。为了高可用性和极致的性能,选择了弱化的一致性模型——最终一致性。因为在 Facebook 的大部分场景下,不可用要比不正确更加糟糕。在大部分常见场景下,TAO 能做到更强的写后读一致性(read after write consistency)。

TAO 中同一份数据,首先,会进行 Master-Slave Region 进行主从备份;其次,在同一 Region 中,会使用 Leader-Follower Cache 做两层缓存。更新时,不同位置的数据不同步,便会造成数据的不一致。在 TAO 中,在更新后给足够时间间隔,所有的数据副本都会趋向一致,并且体现最新更新。而通常,这个时间间隔不会超过 1s 。这在 Facebook 中大多数场景是没有问题的。

对于那些对一致性有特殊要求的场景,应用层可以将请求标记为 critical。TAO 在接到有此标记的请求时,会将其转发到 Master Region 进行处理,进而获取强一致性。

参考

[1] TAO 论文:https://www.usenix.org/system/files/conference/atc13/atc13-bronson.pdf

[2] Facebook 技术博客,TAO——图的威力:https://engineering.fb.com/2013/06/25/core-data/tao-the-power-of-the-graph/

[3] meetup TAO:https://www.notion.so/Meetup-1-Facebook-TAO-28e88836a3f649ba9b3e3ea83858c593

[4] stanford 6.S897 课件:https://cs.stanford.edu/~matei/courses/2015/6.S897/slides/tao.pdf

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
相关文章
|
6月前
|
机器学习/深度学习 自然语言处理 数据可视化
基于图神经网络的自然语言处理:融合LangGraph与大型概念模型的情感分析实践
本文探讨了在企业数字化转型中,大型概念模型(LCMs)与图神经网络结合处理非结构化文本数据的技术方案。LCMs突破传统词汇级处理局限,以概念级语义理解为核心,增强情感分析、实体识别和主题建模能力。通过构建基于LangGraph的混合符号-语义处理管道,整合符号方法的结构化优势与语义方法的理解深度,实现精准的文本分析。具体应用中,该架构通过预处理、图构建、嵌入生成及GNN推理等模块,完成客户反馈的情感分类与主题聚类。最终,LangGraph工作流编排确保各模块高效协作,为企业提供可解释性强、业务价值高的分析结果。此技术融合为挖掘非结构化数据价值、支持数据驱动决策提供了创新路径。
392 6
基于图神经网络的自然语言处理:融合LangGraph与大型概念模型的情感分析实践
|
1月前
|
监控 负载均衡 安全
WebSocket网络编程深度实践:从协议原理到生产级应用
蒋星熠Jaxonic,技术宇宙中的星际旅人,以代码为舟、算法为帆,探索实时通信的无限可能。本文深入解析WebSocket协议原理、工程实践与架构设计,涵盖握手机制、心跳保活、集群部署、安全防护等核心内容,结合代码示例与架构图,助你构建稳定高效的实时应用,在二进制星河中谱写极客诗篇。
WebSocket网络编程深度实践:从协议原理到生产级应用
|
5月前
|
存储 运维 API
HPE OneView 10.0 - HPE 服务器、存储和网络设备集中管理软件
HPE OneView 10.0 - HPE 服务器、存储和网络设备集中管理软件
112 1
|
7月前
|
存储 SQL 运维
中国联通网络资源湖仓一体应用实践
本文分享了中国联通技术专家李晓昱在Flink Forward Asia 2024上的演讲,介绍如何借助Flink+Paimon湖仓一体架构解决传统数仓处理百亿级数据的瓶颈。内容涵盖网络资源中心概况、现有挑战、新架构设计及实施效果。新方案实现了数据一致性100%,同步延迟从3小时降至3分钟,存储成本降低50%,为通信行业提供了高效的数据管理范例。未来将深化流式数仓与智能运维融合,推动数字化升级。
311 0
中国联通网络资源湖仓一体应用实践
|
3月前
|
存储 监控 Linux
Dell OpenManage Enterprise 4.5 - Dell 服务器、存储和网络设备集中管理软件
Dell OpenManage Enterprise 4.5 - Dell 服务器、存储和网络设备集中管理软件
84 0
|
6月前
|
存储 安全 Linux
Dell OpenManage Enterprise 4.4 - Dell 服务器、存储和网络设备集中管理软件
Dell OpenManage Enterprise 4.4 - Dell 服务器、存储和网络设备集中管理软件
190 4
Dell OpenManage Enterprise 4.4 - Dell 服务器、存储和网络设备集中管理软件
|
6月前
|
存储 消息中间件 弹性计算
阿里云服务器ECS计算型c7和通用算力型u1在适用场景、计算性能、网络与存储性能等方面的对比
阿里云ECS服务器u1和c7实例在适用场景、性能、处理器特性等方面存在显著差异。u1为通用算力型,性价比高,适合中小企业及对性能要求不高的场景;c7为企业级计算型,采用最新Intel处理器,性能稳定且强大,适用于高性能计算需求。u1支持多种CPU内存配比,但性能一致性可能受底层平台影响;c7固定调度模式,确保高性能与稳定性。选择时可根据预算与性能需求决定。
338 23
|
8月前
|
安全 自动驾驶 物联网
新四化驱动,如何构建智能汽车的“全场景”可进化互联网络?
在智能化、电动化、网联化、共享化的时代浪潮中,汽车正从单纯的 “机械产品” 进化为先进的 “移动智能终端”。在软件定义汽车(SDV)的崭新时代,每一次 OTA 升级的顺利完成、每一秒自动驾驶的精准决策、每一帧车载娱乐交互的流畅呈现,都离不开一张实时响应、全域覆盖、安全可靠的广域网络。
|
8月前
|
监控 算法 安全
公司电脑网络监控场景下 Python 广度优先搜索算法的深度剖析
在数字化办公时代,公司电脑网络监控至关重要。广度优先搜索(BFS)算法在构建网络拓扑、检测安全威胁和优化资源分配方面发挥重要作用。通过Python代码示例展示其应用流程,助力企业提升网络安全与效率。未来,更多创新算法将融入该领域,保障企业数字化发展。
191 10
|
8月前
|
缓存 边缘计算 安全
阿里云CDN:全球加速网络的实践创新与价值解析
在数字化浪潮下,用户体验成为企业竞争力的核心。阿里云CDN凭借技术创新与全球化布局,提供高效稳定的加速解决方案。其三层优化体系(智能调度、缓存策略、安全防护)确保低延迟和高命中率,覆盖2800+全球节点,支持电商、教育、游戏等行业,帮助企业节省带宽成本,提升加载速度和安全性。未来,阿里云CDN将继续引领内容分发的行业标准。
476 7