Pergunta

Esta é uma questão sobre compensações.

Imagine uma rede social.Cada usuário possui uma mensagem de status, que pode ser alterada a qualquer momento.Sempre que ele muda, todos os seus amigos são notificados através de um mural (como no Facebook).

Para fazer isso funcionar.Temos 3 tabelas Usuários (id, nome), FriendLists (userId, friendUserId), Notificações (?).

Agora vamos supor que cada usuário tenha aproximadamente 50 amigos em sua lista de amigos.Estou diante do dilema - como implementar a tabela de Notificações.


1ª opção

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 notificações:

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

Neste caso, para cada mudança de status criamos 50 registros (assumindo 50 amigos).Isto é mau.Porém o bom é que recuperar notificações de um usuário específico é muito rápido, pois temos um índice clusterizado no toUserId.

2ª opção

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 notificações:

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

Aqui inserimos apenas um único registro por atualização de status.Isso é bom.O ponto ruim é que a recuperação das notificações será mais lenta, pois os registros não são agrupados por toUserId.


Recebendo notificações é o mesmo para ambos os métodos:

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

Então, qual é a sua opinião sobre isso?

Foi útil?

Solução

Primeiro, as leituras sempre serão esmagadoras em comparação com as gravações, porque cada 'parede' será vista muito mais vezes do que será atualizada. Então é melhor você fazer leituras rapidamente.

Segundo, um dos problemas inerentes a esse tipo de grandes sites de redes sociais é a distribuição de dados (sharding, particionamento, nenhum banco de dados único será capaz de armazenar todas as contas, todos os amigos, todas as notificações), o que significa que quando uma nova notificação é colocado em uma parede, os amigos precisam ser notificados em outro servidores. Isso implica que as atualizações são assíncronas e baseadas em mensagens de qualquer maneira.

Então, eu definitivamente iria com uma estrutura otimizada para leitura.

Eu recomendo que você analise as apresentações públicas feitas por várias pessoas envolvidas na arquitetura de sites como Facebook e MySpace, como Este Christa Stelzmuller. Eles explicam muito do pensamento e raciocínio que entram em seu design.

Outras dicas

As atualizações são muito lentas em comparação com as seleções ... algumas ordens de magnitude. Além disso, à medida que o seu site escala, você estará em cache todas as suas buscas na memória, para que a velocidade das seleções seja trivial.

Nessa situação, parece uma má ideia criar um índice clusterizado em (toUser,identity) porque um índice clusterizado realmente deveria ser inserido em ordem crescente.É claro que o SQL cuidará de manter a tabela classificada, mas isso tem um alto custo de desempenho (que é o ponto da sua pergunta). Mas, em geral, inserções que são conhecidas antecipadamente por não estarem em nenhuma ordem específica não são recomendadas para índices agrupados.Aqui está um muito bom três papel artigo sobre recomendações de índice clusterizado.

Dito isto, eu manteria a coluna de identidade como seu índice clusterizado e criaria um índice não clusterizado em toUserId e talvez uma coluna de data e hora.Ao incluir uma coluna de data e hora, você pode consultar dados recentes com mais eficiência.

Em relação às atualizações lentas, as atualizações de status em sites de redes sociais são uma situação perfeita para filas de mensagens.Dessa forma, você pode ajustar o banco de dados conforme necessário para tornar as leituras mais rápidas e, se isso afetar o desempenho de gravação, o usuário não terá que sofrer.Do ponto de vista deles, a atualização foi instantânea, embora possa levar alguns momentos para "grudar".

Para bancos de dados muito grandes, recorrerei aos gurus do SQL, que podem falar sobre estratégias de particionamento (tabelas menores e mais gerenciáveis ​​para dados mais recentes, tabelas maiores/fortemente indexadas para dados mais antigos) e soluções de replicação.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top