Facebook数据库设计?


133

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

我认为用户表是这样的:

user_email PK
user_id PK
password 

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

如何将所有朋友连接到该用户?

像这样吗

user_id
friend_id_1
friend_id_2
friend_id_3
friend_id_N 

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


13
有一个Facebook工程页面,其中包含很多此类信息,但不完全是您要问的信息。您可能想在那里问一问,看看是否可以得到答案。 facebook.com/FacebookEngineering
John Meagher

1
谷歌graph database。它肯定不是 RDBMS。

Answers:


90

保留一个朋友表,该表包含用户ID,然后是朋友的UserID(我们将其称为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

这表明鲍勃是乔恩和乔的朋友,乔恩也是乔的朋友。在此示例中,我们将假定友谊始终是两种方式,因此您无需在表中显示诸如(2,1)或(3,2)之类的行,因为它们已经在另一个方向上表示了。例如,在友谊或其他关系不是明确的双向关系的示例中,您还需要使那些行指示双向关系。


8
想想这是多么低效-您必须对多对多的列进行析取查询,平均使搜索时间加倍。
Anthony Bishopric

2
就个人而言,我不希望这两个字段构成复合主键。绝对是唯一的密钥。绝对是该唯一键上的聚集索引。但是我也将某种非复合身份作为带有非聚集索引的PK。这将使需要“朋友关系ID” FK的其他表可以轻松地绑定到该表,并且可能触发各种触发器来级联朋友,取消朋友等事件。–
Jesse C. Slicer

1
它说Facebook有大约1'000'000用户。如果普通用户有100个朋友,则意味着该表将包含100'000'000'000行。MySQL分区?
veidelis,2014年

忘记这种方法。如果您获得大量用户,则肯定会变得非常缓慢。查看我的答案,然后尝试自己进行基准测试。我已经与1万名用户和250万个友谊连接进行了一些基准测试,结果令人失望。如果您经营一个小型社区,它将可以正常工作,但是要考虑性能问题。
burzum 2015年

7
您可以确定facebook不会为此使用RDBMS,众所周知,他们,twitter和需要运行此类查询的其他所有人都使用某种形式的图形数据库。至少有69个人从未从事过任何规模的工作,或者不知道如何进行大规模的数学运算。

51

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

Facebook架构


7
这是一个类图,而不是数据库架构
Lemon Juice 2015年

2
那么每个“用户”都会有自己的专用数据库吗?喜欢上面的那个吗?如何运作?例如,当用户登录FB时,检查其是否为有效的User + Pass,然后如果有效,facebook会将其重定向至该数据库,然后显示上述数据库中的所有内容
James111

这仅存储与用户有关的信息,我是在专门搜索帖子及其受众吗?
Waseem Ahmad Naeem

47

TL; DR:

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

长答案:

我本人对此进行了一些研究,因为我很好奇它们如何处理大量数据并快速进行搜索。我见过有人抱怨定制的社交网络脚本会随着用户群的增长而变慢。在我仅用1万个用户和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
    )

我真的建议您创建至少包含1万条用户记录的示例数据,并且每条记录至少具有250个朋友连接,然后运行此查询。在我的机器(i7 4770k,SSD,16gb RAM)上,该查询的结果约为0.18秒。也许可以对其进行优化,但我不是DB天才(欢迎提出建议)。但是,如果按比例缩放是线性的,那么对于100k用户而言,您已经达到1.8秒,对于100万用户而言已经为18秒。

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

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

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


所以..你曾经写这篇文章吗?
FlowUI。SimpleUITesting.com

1
不,除了编程我还很忙,没有时间和精力去做。此处的答案包含了您是否想要实现表演者朋友关联所需要知道的一切。缓存每个用户的好友列表,或者将关系数据库的一部分或全部映射到图形并查询图形数据库。您可以为此使用OrientDB或Neo4j。我很想编写自己的开源社交网络软件,但还有很多其他事情要做。无论您做什么:做基准测试。:)
burzum '16

仍然没有。但是OrientDB文档说明了朋友之间的联系,一旦了解了基础知识,其他所有内容都可以建模。orientdb.com/docs/2.1/Tutorial-Working-with-graphs.html如果您想使用关系数据库作为基础,则只需在“保存后”和“删除后”回调中添加一些代码即可更新您的图形数据库(用于读取数据)。如果您没有这样的回调实现它们,但我想几乎所有类型的ORM实现和框架都具有类似的东西。实际上,OrientDB也可以存储文档。
burzum

1
所以..你曾经写这篇文章吗?
康纳·格尼

1
仍然没有,但是我们在做类似的工作:我们将关系数据映射到Elastic Search索引,正如我之前在评论中所写的那样,这仅是在执行某些操作后获取要存储在索引或图中的数据的问题(在本例中为afterSave()/ afterDelete()回调),然后更新索引或图形。很简单吗?:)顺便说一句,可以对朋友列表进行处理,将它们存储在ES,图形或基于内存的缓存中(只要您有足够的RAM)并不重要。这确实并不难,最困难的部分是在成长时使整个事物扩展。
burzum

32

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

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


40
我有种感觉,您将不得不为这里的某些人解释更多。
TheTXI

4
我认为一个更有趣的问题是如何以一种易于搜索和更新的方式来保持如此庞大的结构(我们正在谈论2亿个节点和数十亿条边)。
Dirk Vollmar 09年

1
@divo:巧妙地使用索引和分区。
belgariontheking

20

这很可能是多对多关系:

FriendList(表)

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

编辑

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

用户(表)

user_id PK
user_email
password

4
尽管这当然是最合理的,但考虑到Facebook拥有多少用户以及每个Facebook用户拥有多少朋友,我认为这种表现将是可怕的。
Kevin Pang)2009年

17

看看这些描述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

您会看到,这些公司正在处理数据仓库,分区数据库,数据缓存和其他更高层次的概念,这比我们大多数人每天都不曾处理过的要多。或者至少,也许我们不知道我们这样做。

前两篇文章中有很多链接,这些链接应该使您有更多的了解。

更新10/20/2014

Murat Demirbas撰写了关于

  • TAO:用于社交图的Facebook分布式数据存储(ATC'13)
  • F4:Facebook温暖的BLOB存储系统(OSDI'14)

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

高温超导


9

不可能从RDBMS检索用户朋友数据,而要获取恒定时间超过10亿的数据,因此Facebook使用哈希数据库(没有SQL)实现了这一点,他们开源了名为Cassandra的数据库。

因此,每个用户都有自己的密钥,并且朋友的详细信息在队列中。知道cassandra是如何工作的:

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


非常有趣,谢谢你的朋友。他们何时从sql切换到cassandra?你碰巧知道吗?
马林

1
请注意:Posterous Spaces已死...因此,链接。
TechNyquist'7


5

您正在寻找外键。基本上,除非数据库具有自己的表,否则您无法在数据库中拥有数组。


模式示例:

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

5
为什么要下票?至少让别人知道你为什么拒绝他们。
Sasha Chedygov,2009年

3
@freak:为什么?在此站点上进行投票的整个概念都是匿名的。您为什么感觉mallfist有权获得任何东西?
GEOCHET

4
尤其是当它是一个有效的答案并且得到其他答案的回响时(尽管我没有从他们那里抄袭,但是当我回答时,那里没有答案)
Malfist

4
@TheTXI:我认为对下注的评论是有礼貌的,尤其是对于显然不值得他们回答的答案,但我也同意不应强制发表评论。
罗伯特·S。2009年

2
对不明显的答案进行匿名投票的人是那些担心如果留下评论解释投票失败的浅薄推理的人。
Vinayak


1

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


24
永不忘记!我父亲之所以去世,是因为一个数据库表在垂直方向上对于列而言已经增长得太远了。我会想念你的爸爸。
belgariontheking

1
嗯,为什么要投票?而且上面这一条评论没有任何意义。
尼尔N

2
不,评论没有道理。似乎有人试图变得有趣,所以不要介意。
Dirk Vollmar 09年

0

关于多对多表的性能,如果您有2个32位int链接用户ID,则200,000,000个用户的平均数据存储量(平均每个200个朋友)不足300GB。

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


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(存储为会话属性)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.