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

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS PostgreSQL,集群系列 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

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
1天前
|
自动驾驶 物联网 5G
深入探索5G网络中的网络切片技术及其应用场景
深入探索5G网络中的网络切片技术及其应用场景
14 3
|
2天前
|
存储 监控 安全
网络安全与信息安全:保护数据的重要性与实践措施
本文深入探讨了网络安全和信息安全领域内的关键概念,包括网络漏洞、加密技术以及安全意识的重要性。通过分析这些要素,文章提供了实用的策略来增强个人和企业的数据保护能力。我们将探讨如何识别和防范潜在的网络威胁,并强调了持续更新安全知识和采取预防措施的必要性。
|
13天前
|
云安全 安全 网络安全
云计算环境下的网络安全策略与实践
【9月更文挑战第6天】在数字化浪潮中,云计算已成为企业转型和创新的强大引擎。随之而来的网络安全挑战亦日益突出,成为制约云服务发展的关键因素。本文深入探讨了云计算环境中的安全风险,并提出了一系列切实可行的网络安全策略。从基础的数据加密到高级的身份验证机制,再到细致的访问控制和入侵检测系统的应用,我们细致勾勒出一幅全面的云计算安全蓝图。通过实例分析,文章揭示了安全策略在实际场景中的应用效果,并对未来云计算安全的发展趋势进行了前瞻性的预测。旨在为云计算服务提供商和用户双方提供指导,共同构筑更为坚固的网络安全防线。
|
11天前
|
机器学习/深度学习 数据采集 数据可视化
深度学习实践:构建并训练卷积神经网络(CNN)对CIFAR-10数据集进行分类
本文详细介绍如何使用PyTorch构建并训练卷积神经网络(CNN)对CIFAR-10数据集进行图像分类。从数据预处理、模型定义到训练过程及结果可视化,文章全面展示了深度学习项目的全流程。通过实际操作,读者可以深入了解CNN在图像分类任务中的应用,并掌握PyTorch的基本使用方法。希望本文为您的深度学习项目提供有价值的参考与启示。
|
15天前
|
监控 安全 网络安全
云计算与网络安全的融合之路:探索云服务中的信息安全实践
【9月更文挑战第3天】在数字化转型的浪潮中,云计算已成为现代企业不可或缺的技术基石。然而,随着数据和应用逐渐迁移至云端,网络安全和信息安全的挑战亦随之升级。本文将深入探讨云计算环境下的网络安全挑战,并分享如何通过策略和技术手段加强云服务的安全防护,确保企业资产与数据的完整性、可用性和保密性。
34 5
|
13天前
|
安全 算法 网络安全
网络安全与信息安全:知识分享与实践
本文将深入探讨网络安全漏洞、加密技术和安全意识等方面的知识。我们将分析常见的网络攻击方式,如病毒、木马和黑客攻击,并介绍如何通过防火墙、入侵检测系统等技术手段来防止这些攻击。同时,我们还将讨论密码学在保护数据安全中的重要性,包括对称加密、非对称加密和哈希函数等概念。最后,我们将强调提高个人和组织的安全意识的重要性,包括定期更新软件、使用强密码和多因素认证等措施。
|
19天前
|
自然语言处理 C# 开发者
Uno Platform多语言开发秘籍大公开:轻松驾驭全球用户,一键切换语言,让你的应用成为跨文化交流的桥梁!
【8月更文挑战第31天】Uno Platform 是一个强大的开源框架,允许使用 C# 和 XAML 构建跨平台的原生移动、Web 和桌面应用程序。本文详细介绍如何通过 Uno Platform 创建多语言应用,包括准备工作、设置多语言资源、XAML 中引用资源、C# 中加载资源以及处理语言更改。通过简单的步骤和示例代码,帮助开发者轻松实现应用的国际化。
28 0
|
19天前
|
存储 安全 网络安全
云端防御:云计算时代的网络安全策略与实践
【8月更文挑战第31天】 在数字化转型的浪潮中,云计算已成为企业信息技术架构的核心。然而,云服务的广泛应用也带来了新的安全挑战。本文将探讨云计算环境中的网络安全问题,分析云服务的安全风险,并提出相应的防护措施。文章旨在通过技术解析和代码示例,为读者提供一套实用的云网络安全策略,确保数据安全和业务连续性。
|
19天前
|
监控 安全 网络安全
云计算环境下的网络安全策略与实践
【8月更文挑战第31天】 在数字化浪潮中,云计算以其高效、灵活的特点成为企业IT架构的首选。然而,随之而来的网络安全挑战也日益严峻。本文将深入探讨云服务中的安全风险,提出相应的防护措施,并通过代码示例展示如何在云计算环境中实现信息安全的最佳实践。我们将从基础的安全配置谈起,逐步过渡到复杂的网络攻击防御策略,旨在为读者提供一套系统的云计算网络安全解决方案。
|
19天前
|
监控 安全 网络安全
云计算与网络安全:探索云服务中的信息安全实践
【8月更文挑战第31天】 在数字化转型的浪潮中,云计算成为企业信息技术架构的核心。然而,随之而来的网络安全挑战也日益严峻。本文将深入探讨云计算环境中的网络安全问题,并提供实用的信息安全策略和代码示例,旨在帮助企业构建更加安全的云环境。