141

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

我认为用户表是这样的:

user_email PK
user_id PK
password 

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

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

像这样的东西?

user_id
friend_id_1
friend_id_2
friend_id_3
friend_id_N 

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

4

13 回答 13

91

保留一个朋友表,其中包含用户 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),因为它们已经在另一个方向上表示了。对于友谊或其他关系不是明确的双向关系的示例,您还需要有这些行来指示双向关系。

于 2009-06-17T19:21:32.223 回答
59

TL;博士:

他们使用带有缓存图的堆栈体系结构来存储堆栈底部 MySQL 之上的所有内容。

长答案:

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

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

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

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

让我们看看这个,朋友关系在左上角:

在此处输入图像描述

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

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

我不能告诉你如何构建它以使其表现良好,但它显然需要一些试验和错误以及基准测试。

这是我对朋友的朋友的结果的令人失望的测试:

数据库架构:

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/是一个不错的网站,涵盖了我之前提到的一些主题。

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

于 2015-02-26T00:34:40.130 回答
53

查看由 Anatoly Lubarsky 进行反向工程的以下数据库模式:

Facebook 架构

于 2009-07-13T16:18:01.610 回答
37

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

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

于 2009-06-17T19:21:38.120 回答
21

这很可能是多对多关系:

好友列表(表格)

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

编辑

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

用户(表)

user_id PK
user_email
password
于 2009-06-17T19:20:46.110 回答
19

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

还有“大数据: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

高温高压

于 2009-06-17T21:38:22.680 回答
12

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

所以每个用户都有自己的密钥和队列中的朋友详细信息;要了解 cassandra 的工作原理,请查看以下内容:

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

于 2010-08-20T05:51:40.500 回答
7

最近 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 可以找到更长的论文

于 2013-06-28T18:07:40.250 回答
6

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

它与关系数据库无关。

谷歌图数据库。

于 2011-04-12T12:06:36.310 回答
5

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


示例架构:

    用户表
        用户名 PK
        其他数据
    朋友桌
        userID -- 代表有朋友的用户的用户表的 FK。
        friendID -- FK 到代表朋友的用户 ID 的用户表
于 2009-06-17T19:22:22.830 回答
1

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

于 2009-06-17T19:40:46.723 回答
0

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

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

于 2009-06-18T00:17:02.597 回答
0

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

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

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

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

于 2011-10-29T16:59:57.817 回答