2

トレードオフについての質問です。

ソーシャルネットワークを想像してみてください。各ユーザーにはステータス メッセージがあり、いつでも変更できます。彼がそれを変更するときはいつでも、彼のすべての友人は壁を通して通知されます (Facebook のように)。

これを機能させるには。Users(id, name)、FriendLists(userId、friendUserId)、Notifications(?) の 3 つのテーブルがあります。

ここで、各ユーザーのフレンド リストに約 50 人のフレンドがいるとします。私はジレンマに直面しています - 通知テーブルを実装する方法。


最初のオプション

CREATE TABLE Notifications
(
toUserId bigint NOT NULL,
[identity] bigint IDENTITY(1,1) NOT NULL,
fromUserId bigint NOT NULL,
data varchar(256) NOT NULL,
CONSTRAINT [PK_Notifications] PRIMARY KEY CLUSTERED (toUserId, [identity])
)

通知を送信:

-- Get all friends of @fromUserId.
WITH Friends AS
   (SELECT FriendLists.friendUserId
 FROM FriendLists
 WHERE userId = @fromUserId)
-- Send updates to all friends.
SELECT
 friendUserId as toUserId,
 @fromUserId as fromUserId,
 @data as data
INTO Notifications
FROM Friends

この場合、ステータスの変更ごとに 50 のレコードを作成します (50 人の友人を想定)。これは悪いです。ただし、良い点は、toUserId にクラスター化されたインデックスがあるため、特定のユーザーの通知を取得するのが非常に高速であることです。

2番目のオプション

CREATE TABLE Notifications
(
toUserId bigint NOT NULL,
[identity] bigint IDENTITY(1,1) NOT NULL,
fromUserId bigint NOT NULL,
data varchar(256) NOT NULL,
CONSTRAINT [PK_Notifications] PRIMARY KEY CLUSTERED ([identity])
)
CREATE NONCLUSTERED INDEX [IX_toUserId] ON Notifications (toUserId ASC)

通知を送信:

-- Get all friends of @fromUserId.
WITH Friends AS
   (SELECT FriendLists.friendUserId
 FROM FriendLists
 WHERE userId = @fromUserId)
-- Send updates to all friends.
INSERT INTO Notifications(toUserId, fromUserId, data)
    VALUES(friendUserId, @fromUserId, @data)

ここでは、ステータス更新ごとに 1 つのレコードのみを挿入します。これはいい。悪い点は、toUserId によってレコードがクラスター化されていないため、通知の取得が遅くなることです。


通知の取得は、両方の方法で同じです。

SELECT TOP(50) fromUserId, [identity], data
FROM Notifications
WHERE toUserId  = @toUserId

それで、あなたはこれについてどう思いますか?

4

3 に答える 3

3

まず、書き込みに比べて読み取りは常に圧倒されます。これは、それぞれの「壁」が更新されるよりもはるかに多く見られるためです。したがって、読み取りを非常に高速にする方がよいでしょう。

第二に、この種の大規模なソーシャル ネットワーキング サイトに固有の問題の 1 つは、データの分散 (シャーディング、パーティショニング、すべてのアカウント、すべての友人、すべての通知を格納できる単一のデータベースはありません) です。つまり、新しい通知がウォールに配置すると、他のサーバーで友達に通知する必要があります。これは、更新が非同期であり、とにかくメッセージングに基づいていることを意味します。

したがって、私は間違いなく読み取り用に最適化された構造を使用します。

この Christa Stelzmuller のような、Facebook や MySpace などのサイトのアーキテクチャに関係するさまざまな人々による公開プレゼンテーションを参照することをお勧めします。彼らは、彼らのデザインに込められた多くの考え方と理由を説明しています。

于 2010-01-13T06:20:02.360 に答える
1

更新は SELECT に比べて非常に遅いです...数桁です。さらに、サイトが拡大するにつれて、すべてのフェッチをメモリにキャッシュすることになるため、選択の速度は些細なものになります。

于 2010-01-13T05:10:40.603 に答える
1

この状況では、(toUser,identity) にクラスター化インデックスを作成するのはお勧めできません。クラスター化インデックスは実際には昇順で挿入する必要があるからです。もちろん、SQLはテーブルのソートを維持しますが、これには高いパフォーマンスコストがかかります(これが質問のポイントです)。ただし、一般に、特定の順序ではないことが事前にわかっている挿入は推奨されませんクラスタ化インデックス。これは、クラスター化インデックスの推奨事項に関する非常に優れた3 部構成の 記事です。

そうは言っても、ID 列をクラスター化インデックスとして使用し、toUserId とおそらく datetime 列に非クラスター化インデックスを作成します。datetime 列を含めることで、最近のデータをより効率的にクエリできます。

遅い更新に関して言えば、ソーシャル ネットワーキング サイトでのステータスの更新は、メッセージ キューにとって完璧な状況です。そうすれば、必要に応じてデータベースを調整して読み取りを高速にすることができ、書き込みパフォーマンスに影響がある場合でも、ユーザーが苦しむ必要はありません。彼らの観点からすると、「固まる」までに少し時間がかかるかもしれませんが、更新は瞬時に行われました。

非常に大規模なデータベースについては、パーティショニング戦略 (新しいデータには小さくて管理しやすいテーブル、古いデータには大きくてインデックスの多いテーブル) とレプリケーション ソリューションについて説明できる SQL の達人に任せます。

于 2010-01-13T05:23:25.790 に答える