Pregunta

Esta es una pregunta acerca de las compensaciones.

Imagine una red social. Cada usuario tiene un mensaje de estado, que puede cambiar en cualquier momento. Cada vez que se cambia, todos sus amigos se notifica a través de una pared (como en Facebook).

Para hacer este trabajo. Tenemos 3 mesas Usuarios (id, nombre), FriendLists (identificador de usuario, friendUserId), las notificaciones (?).

Ahora vamos a suponer que cada usuario tiene aproximadamente 50 amigos en su lista de amigos. Estoy frente a la disyuntiva -. La forma de aplicar la tabla Notificaciones


primera opción

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

Enviar notificaciones:

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

En este caso, para cada cambio de estado creamos 50 registros (suponiendo 50 amigos). Esto es malo. Sin embargo, el buen punto es que para recuperar las notificaciones para un usuario específico es muy rápido, ya que tenemos un índice agrupado en la toUserId.

segunda opción

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)

Enviar notificaciones:

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

Aquí sólo insertamos un único registro por actualización de estado. Esto es bueno. El punto negativo es que la recuperación de las notificaciones va a ser más lenta, ya que los registros no son agrupados por toUserId.


Obtención de notificaciones es igual para ambos métodos:

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

Entonces, ¿cuál es tu opinión sobre esto?

¿Fue útil?

Solución

En primer lugar, las lecturas son siempre va a ser abrumador en comparación con las escrituras, ya que cada 'pared' será visto muchas veces más de lo que se actualizará. Así que es mejor hacer maldito lee rápido.

En segundo lugar, uno de los problemas inherentes a este tipo de grandes sitios de redes sociales es la distribución de datos (sharding, partición, ninguna base de datos única volverá a ser capaz de almacenar todas las cuentas, todos los amigos, todas las notificaciones) lo que significa que cuando una nueva notificación se pone en una pared, los amigos tienen que ser avisado en otros servidores. Esto implica cambios son asíncronas y mensajería basada en de todos modos.

Así que sin duda ir con una estructura optimizada para la lectura.

Me gustaría recomendar que se pasa de las presentaciones públicas realizadas por varias personas involucradas en la arquitectura de sitios como Facebook y MySpace, como este de Christa Stelzmuller uno . En ellas se explica una gran parte del pensamiento y el razonamiento que va en su diseño.

Otros consejos

Las actualizaciones son muy lentos en comparación con SELECTs ... algunos órdenes de magnitud. Además, como las escalas de su sitio de almacenamiento en caché que va a todas sus recuperaciones en la memoria, por lo que la velocidad de la selecciona será trivial.

En esta situación, parece como una mala idea para crear un índice agrupado en (TOUSER, identidad) porque un índice agrupado en realidad debería ser insertado en orden ascendente. Por supuesto SQL se encargará de mantener la tabla ordenada, pero esto tiene un costo alto rendimiento (que es el punto de su pregunta.) Pero, en general, las inserciones que se conocen de antemano para estar en ningún orden en particular, no se recomiendan para los índices agrupados. Aquí es una muy buena tres parte artículo acerca de las recomendaciones de índice agrupado.

Una vez dicho esto, me quedo con la columna de identidad como su índice agrupado y crear un índice no agrupado en toUserId y tal vez una columna de fecha y hora. Con la inclusión de una columna de fecha y hora, se puede consultar de forma más eficiente de los datos recientes.

En cuanto a cambios lentos, actualizaciones de estado en los sitios de redes sociales son una situación perfecta para las colas de mensajes. De esa manera se puede sintonizar la base de datos según sea necesario para que se lee rápido y si tiene un impacto en el rendimiento de escritura, el usuario no tendrá que sufrir. Desde su perspectiva la actualización fue instantáneo a pesar de que podría tomar unos momentos para "pegar".

Para grandes bases de datos que va a ceder ante los gurús de SQL que pueden hablar acerca de las particiones estrategias (más pequeñas mesas más manejables para los datos más nuevos, más grandes / mesas fuertemente indexados para los datos más antiguos) y soluciones de replicación.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top