Facebook 数据库设计?

Posted

技术标签:

【中文标题】Facebook 数据库设计?【英文标题】:Facebook database design? 【发布时间】:2010-11-03 18:53:55 【问题描述】:

我一直想知道 Facebook 是如何设计朋友 用户关系的。

我认为用户表是这样的:

user_email PK
user_id PK
password 

我用用户数据(我假设通过用户电子邮件连接的性别、年龄等)计算表格。

它如何将所有朋友与该用户联系起来?

这样的?

user_id
friend_id_1
friend_id_2
friend_id_3
friend_id_N 

可能不会。因为用户的数量是未知的,并且会扩大。

【问题讨论】:

有一个 Facebook 工程页面,其中包含很多此类信息,但并不完全符合您的要求。你可能想在那里问,看看你能不能得到答案。 facebook.com/FacebookEngineering 谷歌graph database。它肯定不是 RDBMS。 【参考方案1】:

TL;DR:

他们使用带有缓存图的堆栈架构,用于存储堆栈底部 mysql 之上的所有内容。

长答案:

我自己对此进行了一些研究,因为我很好奇他们如何处理大量数据并快速搜索。我看到人们抱怨当用户群增长时定制的社交网络脚本变得很慢。在我用只有 10k 个用户和 250 万朋友 连接对自己进行了一些基准测试之后 - 甚至没有试图打扰群组权限、喜欢和墙帖 - 很快结果证明这方法有缺陷。所以我花了一些时间在网上搜索如何做得更好,并看到了这篇官方 Facebook 文章:

TAO: Facebook’s Distributed Data Store for the Social Graph TAO: The power of the graph。

真的建议您在继续阅读之前观看上面第一个链接的演示。这可能是您能找到的关于 FB 如何在幕后工作的最佳解释。

视频和文章告诉你一些事情:

他们在堆栈的最底部使用 MySQL 在 SQL DB 之上有一个 TAO 层,它包含至少两个级别的缓存,并使用图表来描述连接。 我找不到任何关于他们实际用于缓存图表的软件/数据库

我们来看看这个,好友关系在左上角:

嗯,这是一个图表。 :) 它没有告诉您如何 在 SQL 中构建它,有几种方法可以做到,但 this site 有很多不同的方法。 注意: 考虑一下关系数据库就是这样:它被认为是存储规范化数据,而不是图形结构。所以它的性能不如专门的图形数据库。

还要考虑到您必须执行比朋友的朋友更复杂的查询,例如,当您想要过滤您和朋友的朋友喜欢的给定坐标周围的所有位置时。图表是这里的完美解决方案。

我无法告诉您如何构建它以使其运行良好,但显然需要一些试验和错误以及基准测试。

这是我对只是发现朋友的朋友的令人失望的测试:

数据库架构:

CREATE TABLE IF NOT EXISTS `friends` (
`id` int(11) NOT NULL,
  `user_id` int(11) NOT NULL,
  `friend_id` int(11) NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

好友好友查询:

(
        select friend_id
        from friends
        where user_id = 1
    ) union (
        select distinct ff.friend_id
        from
            friends f
            join friends ff on ff.user_id = f.friend_id
        where f.user_id = 1
    )

我真的建议您创建一些示例数据,其中包含至少 10k 个用户记录,并且每个记录至少有 250 个朋友连接,然后运行此查询。在我的机器(i7 4770k、SSD、16gb RAM)上,该查询的结果是 ~0.18 秒。也许可以优化,我不是数据库天才(欢迎提出建议)。但是,如果这是线性比例,对于 10 万用户,您已经是 1.8 秒,对于 100 万用户是 18 秒。

这对于大约 10 万用户来说可能听起来还不错,但考虑到您只是获取朋友的朋友并且没有执行任何更复杂的查询,例如“只显示我朋友的朋友的帖子 + 如果我做权限检查允许或不允许查看其中一些 + 执行子查询以检查我是否喜欢其中任何一个”。您想让数据库检查您是否已经喜欢某个帖子,或者您必须在代码中进行。还要考虑到这不是您运行的唯一查询,并且您在或多或少受欢迎的网站上同时拥有多个活跃用户。

我认为我的回答回答了 Facebook 如何很好地设计他们的朋友关系的问题,但很抱歉,我无法告诉您如何以一种可以快速运行的方式实施它。实施社交网络很容易,但确保其表现良好显然不是 - 恕我直言。

我已经开始尝试使用 OrientDB 来进行图形查询并将我的边映射到底层 SQL DB。如果我完成它,我会写一篇关于它的文章。

如何创建一个性能良好的社交网站?

2021 年 4 月 10 日更新:我可能永远不会写这篇文章;)但这里有一些要点,你可以尝试如何扩展它:

使用不同的读写存储库 基于为此目的而制造的更快的非关系数据库系统构建特定的读取存储库,不要害怕非规范化数据。写入规范化数据库,但从专用视图读取。 使用最终一致性 看看 CQRS 对于社交网络,基于图的读取存储库可能也是个好主意。 将 Redis 用作存储整个序列化数据集的读取存储库

如果您巧妙地结合以上列表中的要点,您可以构建一个非常性能良好的系统。该列表不是“待办事项”列表,您仍然需要理解、思考和熟练使用它! https://microservices.io/ 是一个不错的网站,涵盖了我之前提到的一些主题。

我所做的是存储由聚合生成的事件,并使用项目和处理程序写入上述不同的数据库。很酷的一点是,我可以随时根据需要重新构建我的数据。

【讨论】:

所以..你有没有时间写这篇文章? 不,除了编程之外我还很忙,没有时间和心情去做。如果您想实现高性能的朋友关联,这里的答案包含您需要知道的一切。缓存每个用户的好友列表,或者将您的关系数据库部分或全部映射到图形并查询图形数据库。您可以为此使用 OrientDB 或 Neo4j。我很想编写自己的开源社交网络软件,但还有很多其他事情要做。无论你做什么:做基准测试。 :) 还是没有。但是 OrientDB 文档解释了朋友关系,一旦了解了基础知识,就可以对其他所有内容进行建模。 orientdb.com/docs/2.1/Tutorial-Working-with-graphs.html 如果您想使用关系数据库作为基础,那么您只需要在“保存后”和“删除后”回调中添加一些代码来更新您的图形数据库(您将用于读取数据)。如果你没有这样的回调实现它们,但我想几乎所有类型的 ORM 实现和框架都有类似的东西。实际上 OrientDB 也可以存储文档。 所以..你有没有时间写这篇文章? 仍然没有,但我们在工作中做了类似的事情:我们将关系数据映射到 Elastic Search 索引,正如我之前在评论中所写的,这只是获取您想要存储的数据的问题某个操作后的索引或图形(在我们的例子中为 afterSave() / afterDelete() 回调),然后更新索引或图形。很简单? :) 顺便说一句,朋友列表也可以这样做,将它们存储在 ES、图形或基于内存的缓存中并不重要(只要你有足够的 RAM)。这真的不难,难的是在你成长的时候让整个事情规模化。【参考方案2】:

无法从 RDBMS 中检索用户朋友数据的数据,以获取恒定时间超过 50 亿的数据 所以 Facebook 使用哈希数据库(无 SQL)实现了这一点,他们开源了名为 Cassandra 的数据库。

所以每个用户都有自己的密钥和队列中的朋友详细信息;要知道 cassandra 是如何工作的,请看这个:

http://prasath.posterous.com/cassandra-55

【讨论】:

非常有趣,谢谢我的朋友。他们什么时候从 sql 切换到 cassandra?你碰巧知道吗? 请注意:Posterous Spaces 已死...所以链接。【参考方案3】:

看看这些描述LinkedIn和Digg是如何构建的文章:

http://hurvitz.org/blog/2008/06/linkedin-architecture http://highscalability.com/scaling-digg-and-other-web-applications

还有“大数据:Facebook 数据团队的观点”可能会有所帮助:

http://developer.yahoo.net/blogs/theater/archives/2008/01/nextyahoonet_big_data_viewpoints_from_the_fac.html

此外,还有一篇文章讨论了非关系数据库以及一些公司如何使用它们:

http://www.readwriteweb.com/archives/is_the_relational_database_doomed.php

您会看到,这些公司正在处理数据仓库、分区数据库、数据缓存和其他更高级别的概念,而我们大多数人每天都不会处理这些概念。或者至少,也许我们不知道我们知道。

前两篇文章有很多链接,应该可以让您更深入地了解。

2014 年 10 月 20 日更新

Murat Demirbas在

上写了总结 TAO:Facebook 的社交图谱分布式数据存储 (ATC'13) F4:Facebook 的暖 BLOB 存储系统 (OSDI'14)

http://muratbuffalo.blogspot.com/2014/10/facebooks-software-architecture.html

HTH

【讨论】:

【参考方案4】:

查看以下数据库架构,reverse engineered by Anatoly Lubarsky:

【讨论】:

这是一个类图,不是数据库模式 那么每个“用户”都有自己的专用数据库吗?和上面的一样吗?它将如何运作?例如,当用户登录 FB 检查它是否是有效的用户 + 通行证,然后如果它是有效的 facebook 会将他们重定向到那里的数据库,然后显示上述数据库中的所有内容 这里只存储与用户相关的信息,我是专门搜索帖子及其受众? 谢谢西蒙和布拉德,你们太棒了。上帝保佑你【参考方案5】:

最近 2013 年 6 月的这篇文章详细解释了从关系数据库到具有某些数据类型关联的对象的过渡。

https://www.facebook.com/notes/facebook-engineering/tao-the-power-of-the-graph/10151525983993920

在 https://www.usenix.org/conference/atc13/tao-facebook’s-distributed-data-store-social-graph 上有一篇较长的论文

【讨论】:

【参考方案6】:

可能有一个表,它存储朋友 用户关系,例如“frnd_list”,具有字段“user_id”、“frnd_id”。

每当用户将另一个用户添加为好友时,都会创建两个新行。

例如,假设我的 id 是“deep9c”,我添加了一个 id 为“akash3b”的用户作为我的朋友,然后在表“frnd_list”中创建两个新行,其值为 ('deep9c','akash3b') 和('akash3b','deep9c')。

现在,当向特定用户显示好友列表时,一个简单的 sql 会执行此操作:“select frnd_id from frnd_list where user_id=" 其中是登录用户的 id(存储为会话属性)。

【讨论】:

【参考方案7】:

它是一种图形数据库: http://components.neo4j.org/neo4j-examples/1.2-SNAPSHOT/social-network.html

它与关系数据库无关。

谷歌图数据库。

【讨论】:

【参考方案8】:

关于多对多表的性能,如果您有 2 个 32 位整数链接用户 ID,那么您的 200,000,000 个用户(平均每人 200 个朋友)的基本数据存储空间不到 300GB。

显然,您需要一些分区和索引,并且您不会将其保存在所有用户的内存中。

【讨论】:

【参考方案9】:

请记住,数据库表设计为垂直增长(更多行),而不是水平增长(更多列)

【讨论】:

永远不要忘记!我爸爸死了,因为一个 db 表对于它的列来说垂直增长得太远了。爸爸我会想你的。 嗯,为什么要投反对票?而且上面的评论没有意义。 不,评论没有意义。好像有人想搞笑,所以别介意。【参考方案10】:

我最好的选择是他们创建了一个graph structure。节点是用户,“友谊”是边。

保留一张用户表,保留另一张边表。然后您可以保留有关边缘的数据,例如“他们成为朋友的日子”和“批准状态”等。

【讨论】:

我感觉你需要为这里的一些人多解释一下。 我认为一个更有趣的问题是如何以一种易于搜索和更新的方式保持如此巨大的结构(我们谈论的是 2 亿个节点和数十亿条边)。 @divo:巧妙地使用索引和分区。【参考方案11】:

您正在寻找外键。基本上你不能在数据库中拥有一个数组,除非它有自己的表。


示例架构:

用户表 用户名 PK 其他数据 朋友桌 userID -- 代表有朋友的用户的用户表的 FK。 friendID -- FK 到代表朋友的用户 ID 的用户表

【讨论】:

为什么投反对票?至少让别人知道你为什么不给他们投票。 @freak:为什么?在本网站上投票的整个概念是匿名投票。为什么你觉得 Malfist 有权获得任何东西? 特别是当它是一个有效的答案并且被其他答案所呼应时(虽然我没有从他们那里复制,但当我回答时,那里没有答案) @TheTXI:我认为 cmets 投反对票是一种礼貌,尤其是对于显然不值得他们投票的答案,但我也同意 cmets 不应该是强制性的。 对不明显的答案匿名投反对票的人是那些担心如果他们留下评论来解释投反对票,他们的肤浅推理会被暴露。【参考方案12】:

保留一个朋友表,其中包含用户 ID,然后是朋友的用户 ID(我们将其称为 FriendID)。这两列都是返回用户表的外键。

一些有用的例子:

Table Name: User
Columns:
    UserID PK
    EmailAddress
    Password
    Gender
    DOB
    Location

TableName: Friends
Columns:
    UserID PK FK
    FriendID PK FK
    (This table features a composite primary key made up of the two foreign 
     keys, both pointing back to the user table. One ID will point to the
     logged in user, the other ID will point to the individual friend
     of that user)

示例用法:

Table User
--------------
UserID EmailAddress Password Gender DOB      Location
------------------------------------------------------
1      bob@bob.com  bobbie   M      1/1/2009 New York City
2      jon@jon.com  jonathan M      2/2/2008 Los Angeles
3      joe@joe.com  joseph   M      1/2/2007 Pittsburgh

Table Friends
---------------
UserID FriendID
----------------
1      2
1      3
2      3

这将表明 Bob 是 Jon 和 Joe 的朋友,并且 Jon 也是 Joe 的朋友。在这个例子中,我们将假设友谊总是有两种方式,所以你不需要在表中的一行,例如 (2,1) 或 (3,2),因为它们已经在另一个方向上表示了。对于友谊或其他关系不是明确的双向关系的示例,您还需要有这些行来指示双向关系。

【讨论】:

想想这是多么的低效——你必须对多对多的列进行分离查询,平均搜索时间加倍。 就个人而言,我不希望这两个字段构成复合主键。绝对是独一无二的钥匙。绝对是那个唯一键上的聚集索引。但我也会将某种非复合身份作为具有非聚集索引的 PK。这将允许其他需要“朋友关系 ID”FK 的表轻松绑定到该表,并且可以触发各种触发器来级联加好友、取消加好友等事件。 它说 Facebook 有大约 1'000'000'000 个用户。如果平均用户有 100 个朋友,这意味着该表将包含 100'000'000'000 行。 MySQL 分区? 您可以确定 facebook 没有为此使用 RDBMS,众所周知,他们、twitter 和其他需要运行此类查询的所有人都使用某种风格的图形数据库. 至少有 69 人从未从事过任何规模的工作或不知道如何进行大规模的数学运算。 @user177800 什么是关于大规模设计的不错的入门/论文? ...不是我打算建立下一个 facebook 或任何东西。【参考方案13】:

这很可能是多对多关系:

好友列表(表格)

user_id -> users.user_id
friend_id -> users.user_id
friendVisibilityLevel

编辑

user 表可能没有 user_email 作为 PK,但 可能 作为唯一键。

用户(表)

user_id PK
user_email
password

【讨论】:

虽然这当然是最有意义的,但考虑到 Facebook 有多少用户以及每个 Facebook 用户有多少朋友,我认为性能将是可怕的。

以上是关于Facebook 数据库设计?的主要内容,如果未能解决你的问题,请参考以下文章

Facebook 中的新闻提要数据库设计

具有类似 Facebook 的群组的系统的数据库设计

设计 + Omniauth + Facebook

23个Facebook Paper中的设计细节

使用omniauth-facebook“无效凭据”进行设计

Facebook体验设计小巧思