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ではありません

回答:


90

UserIDを保持するフレンドテーブルを保持し、次にフレンドのUserIDを保持します(これをFriendIDと呼びます)。どちらの列も、Usersテーブルへの外部キーになります。

やや有用な例:

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つの方法であると仮定します。したがって、(2,1)や(3,2)などの行はすでに他の方向で表されているため、行は必要ありません。友情またはその他の関係が明示的に双方向ではない例では、双方向の関係を示すためにそれらの行も必要になります。


8
ただし、これがいかに非効率的であるかを考えてください。多対多の列で論理和クエリを実行する必要があり、平均で検索時間が2倍になります。
Anthony Bishopric

2
個人的には、これらの2つのフィールドで複合主キーを作成したくありません。ユニークなキーです。間違いなく、その一意のキーのクラスター化インデックス。ただし、非クラスタ化インデックスを持つPKとして、ある種の非複合IDも使用します。それは簡単にこのテーブルと様々なトリガに結びつけるために、「友人関係ID」FKを必要とする他のテーブル等、defriending、friendingのカスケードイベントに発火可能性ができるようになる
ジェシーC.スライサーに

1
Facebookには約1,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

2
では、各「ユーザー」には専用のデータベースがありますか?上記のように?それはどのように機能しますか?たとえば、ユーザーがFBにログオンすると、FBが有効なユーザー+パスであるかどうかを確認し、有効な場合はFacebookがデータベースにリダイレクトして、上記のデータベースからすべてを表示します
James111

このストアは、ユーザーに関連する情報のみを保存します。具体的には、投稿とその対象ユーザーを検索していますか?
Waseem Ahmad Naeem

47

TL; DR:

彼らは、スタックのMySQLの最上位より上のすべてに対して、キャッシュされたグラフを備えたスタックアーキテクチャを使用します。

長い答え:

彼らが膨大な量のデータをどのように処理し、すばやく検索するのか知りたくて、自分でこれについていくつかの調査を行いました。ユーザーベースが拡大すると、カスタムメイドのソーシャルネットワークスクリプトが遅くなることについて不満を言う人を見てきました。たった1万人のユーザーと250万人の友達の接続でベンチマークを行ったところ、グループのアクセス許可といいね!と壁の投稿にさえ気を取らなかった後、すぐにこのアプローチに欠陥があることがわかりました。それで、私はそれをより良くする方法についてウェブを検索することに時間を費やして、この公式のFacebookの記事に出くわしました:

私は本当に前に読み続ける上で最初のリンクのプレゼンテーションを見てすることをお勧めいたします。これはおそらく、FBがバックグラウンドでどのように機能するかを説明する最良の説明です。

ビデオと記事はあなたにいくつかのことを教えています:

  • スタックの最下部でMySQLを使用している
  • SQL DBのには、少なくとも2つのレベルのキャッシングを含み、グラフを使用して接続を記述するTAOレイヤーがあります。
  • キャッシュされたグラフに実際に使用しているソフトウェア/ DBについては何も見つかりませんでした

これを見てみましょう。友達の接続は左上です。

ここに画像の説明を入力してください

さて、これはグラフです。:)それはSQLでそれを構築する方法を教えてくれません、それを行うにはいくつかの方法がありますが、このサイトにはかなりの量の異なるアプローチがあります。注意:リレーショナルDBがそれであると考えてください:グラフ構造ではなく、正規化されたデータを格納すると考えられています。そのため、特殊なグラフデータベースほどパフォーマンスが良くありません。

また、たとえば、自分と友達の友達が好きな特定の座標の周りのすべての場所をフィルタリングする場合など、友達の友達よりも複雑なクエリを実行する必要があることも考慮してください。ここではグラフが最適なソリューションです。

うまく機能するようにビルドする方法は説明できませんが、明らかに試行錯誤とベンチマークが必要です。

友達の友達を見つけただけの私の残念なテストは次のとおりです。

DBスキーマ:

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;

Friends of Friendsクエリ:

(
        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の天才ではありません(提案は大歓迎です)。ただし、これが線形に拡張された場合、ユーザーは10万人で1.8秒、100万人で18秒です。

これはまだ10万人までのユーザーには問題ないように聞こえるかもしれませんが、友達の友達を取得しただけで、「友達の友達からの投稿のみを表示する+許可されているか許可されていない場合は許可チェックを行う」などの複雑なクエリを実行しなかったことを考慮してくださいそれらのいくつかを確認するには、サブクエリを実行して、それらのいずれかが気に入ったかどうかを確認します。投稿が気に入ったかどうか、またはコードで行う必要があるかどうかをDBにチェックさせたいとします。また、これが実行する唯一のクエリではなく、多かれ少なかれ人気のあるサイトで同時にアクティブユーザー以上のユーザーがいることも考慮してください。

私の回答は、Facebookがどのように友人関係をうまく設計したかという質問に答えると思いますが、それが高速に機能するように実装する方法を説明できないのは残念です。ソーシャルネットワークの実装は簡単ですが、それがうまく機能することを確認することは明らかにそうではありません-私見。

グラフクエリを行うためにOrientDBの実験を開始し、基になるSQL DBにエッジをマッピングしました。もし私がそれを成し遂げたら、それについての記事を書きます。


そう..あなたは記事を書くために動き回ったことがありますか?
FlowUI。SimpleUITesting.com

1
いいえ、私はプログラミング以外にもかなり忙しく、そのための時間と気分がありません。ここでの回答には、パフォーマンス上の友人の関連付けを実装する場合に知っておく必要があるすべてのものが含まれています。ユーザーごとのフレンドリストをキャッシュするか、リレーショナルDBを部分的にまたは全体的にグラフにマッピングして、グラフDBにクエリを実行します。そのために、OrientDBまたはNeo4jを使用できます。私は自分のオープンソースソーシャルネットワーキングソフトウェアを書きたいと思っていますが、他にもやるべきことがたくさんあります。何をしても:ベンチマークを実行します。:)
burzum

まだ違います。しかし、OrientDBのドキュメントは友達のつながりを説明しており、基本を理解すれば他のすべてをモデル化できます。orientdb.com/docs/2.1/Tutorial-Working-with-graphs.htmlリレーショナルDBを基盤として使用する場合は、「保存後」と「削除後」のコールバックにコードを追加して、グラフDB(データの読み取りに使用)。そのようなコールバックがない場合はそれらを実装しますが、ほぼすべての種類のORM実装とフレームワークがそのようなものを持っていると思います。実際、OrientDBはドキュメントも保存できます。
burzum 2016年

1
そう..あなたは記事を書くために動き回ったことがありますか?
Connor Gurney 2017年

1
それでも違いはありますが、職場で同様のことを行っています。以前のコメントで書いたように、リレーショナルデータをElastic Searchインデックスにマップします。これは、特定のアクションの後に、インデックスまたはグラフに格納するデータを取得するだけの問題です。 (この場合、afterSave()/ afterDelete()コールバック)、インデックスまたはグラフを更新します。ものすごく単純?:)ちなみにフレンドリストでも同じことができます。ES、グラフ、またはメモリベースのキャッシュ(十分なRAMがある限り)に保存してもかまいません。難しいことではありません。難しいのは、成長するときに全体をスケールさせることです。
burzum

32

私の最善の策は、彼らがグラフ構造を作成したことです。ノードはユーザーであり、「友情」はエッジです。

ユーザーのテーブルを1つ保持し、エッジの別のテーブルを保持します。その後、「友達になった日」や「承認済みステータス」など、エッジに関するデータを保持できます。


40
私はあなたがここでいくつかの人々のためにそれをもう少し説明しなければならないだろうと感じています。
TheTXI 2009年

4
より興味深い質問は、このような巨大な構造(2億のノードと数十億のエッジについて話している)を、簡単に検索および更新できる方法で永続化する方法だと思います。
Dirk Vollmar 2009年

1
@divo:インデックスとパーティションの賢い使い方。
belgariontheking 2009年

20

それはおそらく多対多の関係です:

FriendList(テーブル)

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

編集

userテーブルには、おそらく一意のキーとして、おそらく PKとしてのuser_emailがありません。

ユーザー(表)

user_id PK
user_email
password

4
これは確かに最も理にかなっていますが、Facebookのユーザー数と各Facebookユーザーの友達数を考えると、パフォーマンスは恐ろしいものになると思います。
Kevin Pang

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

これらの企業は、データウェアハウス、パーティション化されたデータベース、データキャッシング、その他の高レベルの概念を扱っていることがわかります。あるいは、少なくとも、私たちがそうしていることを知らないかもしれません。

最初の2つの記事には、より多くの洞察を与えるはずの多くのリンクがあります。

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


9

ユーザーの友人のデータをRDBMSから取得できず、一定の時間に5億を超えるデータを取得できないため、Facebookはハッシュデータベース(SQLなし)を使用してこれを実装し、Cassandraと呼ばれるデータベースをオープンソース化しました。

したがって、すべてのユーザーが独自のキーと友達の詳細をキューに入れます。カサンドラの仕組みを知るには、これを見てください:

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


非常に興味深い、私の友人に感謝します。彼らはいつSQLからcassandraに切り替えましたか?たまたま知っていますか?
Marin

1
注意してください:後部スペースは死んでいます...リンク。
TechNyquist 2017

6

この最近の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で利用可能なより長い論文があります


5

外部キーを探しています。基本的に、独自のテーブルがない限り、データベースに配列を持つことはできません。


スキーマの例:

    ユーザーテーブル
        userID PK
        その他のデータ
    フレンズテーブル
        userID-友達がいるユーザーを表すユーザーのテーブルへのFK。
        friendID-友達のユーザーIDを表すユーザーのテーブルへのFK

5
なぜ反対票?少なくとも、あなたが反対票を投じた理由を誰かに知らせてください。
サーシャチェディゴフ2009年

3
@マニア:なぜ?このサイトでの投票の全体的な概念は、匿名で投票することです。なぜあなたはマルフィストが何かを受ける資格があると感じますか?
GEOCHET 2009年

4
特に、それが有効な回答であり、他の回答によってエコーされる場合(私はそれらからコピーしなかったが、私が回答したとき、そこには回答がない)
Malfist

4
@TheTXI:反対票へのコメントは礼儀正しいと思います。特に、当然のことではない回答については礼儀正しいと思いますが、コメントを義務付けるべきではないことにも同意します。
ロバートS.

2
非自明な回答に対して匿名で反対票を投じる人々は、反対票を説明するコメントを残した場合、浅い推論が露呈されることを恐れる人々です。
ビナヤク


1

データベーステーブルは、水平方向(より多くの列)ではなく、垂直方向(より多くの行)に成長するように設計されていることに注意してください


24
決して忘れないでください!私の父は、列に対して垂直方向に大きくなりすぎたdbテーブルが原因で亡くなりました。お父さんがいなくて寂しいです。
belgariontheking 2009年

1
うーん、なぜ反対票か。そして、この上のコメントは意味がありません。
ニールN

2
いいえ、コメントは意味がありません。誰かがおかしくなろうとしたようなので、気にしないでください。
Dirk Vollmar 09年

0

多対多のテーブルのパフォーマンスに関して、ユーザーIDをリンクする32ビットの整数が2つある場合、200,000,000人のユーザーの基本的なデータストレージは、平均して200人の友達を1人あたり300GB未満にします。

明らかに、いくつかのパーティション化とインデックス付けが必要であり、それをすべてのユーザーのためにメモリに保持するつもりはありません。


0

おそらく、「<user> id」、「frnd_id」というフィールドを持つ、友達<->ユーザーの関係を格納するテーブル(「frnd_list」など)があります。

ユーザーが別のユーザーを友達として追加すると、2つの新しい行が作成されます。

たとえば、IDが「deep9c」で、IDが「akash3b」のユーザーを友達として追加すると、2つの新しい行がテーブル「frnd_list」に作成され、( 'deep9c'、 'akash3b')と( 'akash3b '、' deep9c ')。

これで、特定のユーザーに友達リストを表示するときに、単純なsqlは次のように実行します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.