Question

Ceci est une question sur des compromis.

Imaginez un réseau social. Chaque utilisateur dispose d'un message d'état, qu'il peut changer à tout moment. Chaque fois qu'il ne le changer, tous ses amis sont informés à travers un mur (comme dans Facebook).

Pour faire ce travail. Nous avons 3 tables utilisateurs (id, nom) FriendLists (userId, friendUserId), Notifications (?).

Maintenant, supposons que chaque utilisateur a environ 50 amis dans sa liste d'amis. Je suis confronté au dilemme -. Comment implémenter la table Notifications


1ère option

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])
)

Envoyer des notifications:

-- 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

Dans ce cas, pour chaque changement d'état, nous créons 50 dossiers (si on prend 50 amis). C'est mauvais. Cependant, le bon point est que pour récupérer des notifications pour un utilisateur spécifique, il est très rapide, puisque nous avons un index ordonné en clusters sur le toUserId.

2ème option

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)

Envoyer des notifications:

-- 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)

Ici, nous insérons seulement un seul enregistrement par mise à jour de statut. C'est bon. Le mauvais point est que la récupération des notifications va être plus lent, car les dossiers ne sont pas regroupés par toUserId.


Notifications Obtenir est la même pour les deux méthodes:

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

Alors, quelle est votre opinion à ce sujet?

Était-ce utile?

La solution

Tout d'abord, lit vont toujours être écrasante en comparaison avec les écritures, parce que chacun sera vu « mur » beaucoup plus de fois qu'il sera mis à jour. Donc, il vaut mieux se lit sacrément rapide.

En second lieu, l'un des problèmes inhérents à ce genre de grands sites de réseaux sociaux est la distribution des données (sharding, partitionnement, aucune base de données ne sera jamais capable de stocker tous les comptes, tous les amis, toutes les notifications) ce qui signifie que lorsque une nouvelle notification est mise sur un mur, les amis doivent être notifiées sur autres serveurs . Cela implique des mises à jour sont asynchrones et la messagerie en fonction de toute façon.

Je serais certainement aller avec une structure optimisée pour la lecture.

Je vous recommande de passer au cours des présentations publiques faites par diverses personnes impliquées dans l'architecture des sites tels que Facebook et MySpace, comme un de cette Christa Stelzmüller. Ils expliquent une grande partie de la pensée et de raisonnement qui va dans leur conception.

Autres conseils

Mises à jour sont très lent par rapport à SELECTs ... quelques ordres de grandeur. De plus, comme votre site échelles, vous serez en mémoire cache toutes vos récupérations en mémoire, de sorte que la vitesse de trivial sera sélectionne.

Dans cette situation, il semble être une mauvaise idée de créer un index cluster sur (TOUSER, identité) parce qu'un index cluster doit vraiment être inséré dans l'ordre croissant. Bien sûr SQL prendre soin de garder la table triée, mais cela a un coût élevé de performance (qui est le point de votre question.) Mais en général, des inserts qui sont en avance connus de temps pour être dans aucun ordre particulier ne sont pas recommandés pour index clusterisés. Voici un très bon trois partie article sur les recommandations d'index ordonné en clusters.

Cela dit, je bâton avec la colonne d'identité en tant que votre index ordonné en clusters et créer un index non cluster sur toUserId et peut-être une colonne datetime. En incluant une colonne datetime, vous pouvez plus efficacement interroger les données récentes.

En ce qui concerne les mises à jour lentes, les mises à jour de statut sur les sites de réseaux sociaux sont une situation idéale pour les files d'attente de messages. De cette façon, vous pouvez régler la base de données que nécessaire pour faire des lectures rapides et si elle a un impact sur les performances d'écriture, l'utilisateur ne sera pas à souffrir. De leur point de vue la mise à jour a été instantanée, même si cela peut prendre quelques instants pour « coller ».

Pour les bases de données très importantes, je vais céder la parole aux gourous SQL qui peuvent parler de stratégies de partitionnement (petites tables de plus faciles à gérer pour des données plus récentes, les grandes tables / fortement indexées pour des données plus anciennes) et des solutions de réplication.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top