Uau, esta é uma pergunta simples, com uma enorme variedade de respostas possíveis. A parte mais explícita da sua pergunta pergunta se é mais escalável interagir com seu banco de dados diretamente ou através de um serviço da web. Essa resposta é simples: consulte o banco de dados diretamente. Passar pelo serviço da Web adiciona um monte de latência que é completamente desnecessária para o código que opera atrás de um firewall (em geral). Um serviço da web, por exemplo, exige que algum componente receba uma solicitação, desserialize-a, consulte o banco de dados, serialize uma resposta e a retorne. Portanto, se todo o seu código estiver operando atrás de um firewall, salve-se do problema e consulte o banco de dados diretamente.
Tornar um site escalável, no entanto, vai muito além da pergunta que você fez inicialmente. Então, perdoe-me se eu sair pela tangente aqui, mas achei que seria útil, considerando que você mencionou o Facebook em particular.
Eu recomendaria que você lesse o trabalho e as ferramentas criadas por Brad Fitzpatrick (fundador do LiveJournal e agora no Google). Quando trabalhei com ele no Six Apart, aqui estão algumas das coisas que aprendi com ele e sobre a arquitetura do LiveJournal que a tornou tão escalável.
Use tabelas de banco de dados estreitas em oposição a tabelas amplas . O que foi fascinante foi aprender o que motivou essa arquitetura, que estava criando um sistema que era fácil e rápidoatualizado. Se você usar tabelas amplas, ou tabelas para as quais cada campo ou propriedade é uma coluna da tabela, quando chegar a hora de atualizar o esquema do banco de dados, por exemplo, adicionar uma nova coluna, o sistema precisará bloquear a tabela enquanto o esquema mudança é implementada. Ao operar em escala, isso significaria que uma simples alteração no esquema do banco de dados poderia resultar em uma grande interrupção do banco de dados. O que é péssimo, obviamente. Uma tabela estreita, por outro lado, simplesmente armazena cada propriedade individual associada a um objeto como uma única linha no banco de dados. Portanto, quando você deseja adicionar uma nova coluna ao banco de dados, tudo o que você precisa fazer é INSERIR registros em uma tabela, que é uma operação sem bloqueio. Ok, isso é um pouco de fundo, vamos ver como esse modelo realmente se traduz em sistemas de trabalho como o LiveJournal.
Digamos que você queira carregar as últimas 10 entradas do diário no blog de uma pessoa e digamos que cada entrada do diário tenha dez propriedades. Em um layout clássico de tabela ampla, cada propriedade se correlacionaria com uma coluna em uma tabela. Um usuário consultaria a tabela uma vez para buscar todos os dados necessários. A consulta retornaria 10 linhas e cada linha teria todos os dados necessários (por exemplo, entradas SELECT * FROM ORDER BY date LIMIT 10). Em um layout de tabela estreito, porém, as coisas são um pouco diferentes. Neste exemplo, existem duas tabelas: a primeira tabela (tabela A) armazena critérios simples pelos quais você deseja pesquisar, por exemplo, o ID da entrada, o ID do autor, a data da entrada etc. Uma segunda tabela (tabela B), em seguida, armazena todas as propriedades associadas a uma entrada. Esta segunda tabela possui três colunas: entry_id, chave e valor. Para cada linha na tabela A, haveria 10 linhas na tabela B (uma linha para cada propriedade). Portanto, para buscar e exibir as últimas dez entradas, você precisaria de 11 consultas. A primeira consulta fornece a lista de IDs de entrada e as próximas dez buscam as propriedades associadas a cada uma das entradas retornadas na primeira consulta.
"Santo Moly!" você diz: "como na Terra isso pode ser mais escalável ?!" É totalmente contra-intuitivo, certo? No primeiro cenário, tivemos apenas uma consulta ao banco de dados, mas na segunda solução "mais escalável", temos 11 consultas ao banco de dados. Isso não faz sentido. A resposta para essa pergunta depende inteiramente da próxima bala.
Use o memcache liberalmente. Caso você não saiba, o memcache é um sistema de armazenamento em cache distribuído, sem estado e de baixa latência, baseado em rede. É usado pelo Facebook, Google, Yahoo e praticamente todos os sites populares e escaláveis do planeta. Foi inventado por Brad Fitzpatrick parcialmente para ajudar a compensar a sobrecarga do banco de dados inerente a um design de banco de dados de tabela estreita. Vamos dar uma olhada no mesmo exemplo, como discutido no item 1 acima, mas desta vez, vamos apresentar o memcache.
Vamos começar quando um usuário visita uma página pela primeira vez e nada está no cache. Você começa consultando a tabela A, que retorna os IDs das 10 entradas que você deseja exibir na página. Para cada uma dessas entradas, você consulta o banco de dados para recuperar as propriedades associadas a essa entrada e, em seguida, usá-las constitui um objeto com o qual seu código pode interagir (por exemplo, um objeto). Em seguida, você esconde esse objeto (ou uma forma serializada desse objeto) no memcache.
Na segunda vez que alguém carrega a mesma página, você começa da mesma maneira: consultando a tabela A para obter a lista de IDs de entrada que você exibirá. Para cada entrada, você primeiro acessa o memcache e diz: "você tem a entrada #X no cache?" Se sim, o memcache retornará o objeto de entrada para você. Caso contrário, será necessário consultar o banco de dados novamente para buscar suas propriedades, constituir o objeto e armazená-lo no memcache. Na maioria das vezes, na segunda vez que alguém visita a mesma página, há apenas uma consulta ao banco de dados, todos os outros dados são extraídos diretamente do memcache.
Na prática, o que acabou acontecendo na maior parte do LiveJournal é que a maioria dos dados do sistema, especialmente os menos voláteis, foi armazenada em cache no memcache e as consultas extras ao banco de dados necessárias para suportar o esquema de tabela restrita foram quase completamente compensadas.
Esse design tornou mais fácil a solução do problema associado à montagem de uma lista de postagens associadas a todos os seus amigos em um fluxo ou "mural" .
Em seguida, considere particionar seu banco de dados. O modelo discutido acima apresenta outro problema, e suas tabelas estreitas tendem a ser muito grandes / longas. E quanto mais linhas essas tabelas apresentarem, mais difíceis serão as tarefas administrativas. Para compensar isso, pode fazer sentido gerenciar o tamanho de suas tabelas particionando-as de alguma maneira, para que grupos de usuários sejam atendidos por um banco de dados e outro grupo de usuários seja atendido por um banco de dados separado. Isso distribui a carga no banco de dados e mantém as consultas eficientes.
Finalmente, você precisa de índices impressionantes. A velocidade das suas consultas dependerá em grande parte de quão bem as tabelas do seu banco de dados estão indexadas. Não vou gastar muito tempo discutindo o que é um índice, exceto para dizer que é muito parecido com um sistema gigante de catálogo de cartões para tornar mais eficiente a localização de agulhas no palheiro. Se você usa o mysql, recomendo ativar o log de consultas lentas para monitorar as consultas que demoram muito tempo para serem atendidas. Quando uma consulta aparecer no seu radar (por exemplo, porque é lenta), descubra qual índice você precisa adicionar à tabela para acelerar.
"Obrigado por todo esse excelente histórico, mas, caramba, isso é muito código que terei que escrever."
Não necessariamente. Muitas bibliotecas foram escritas para facilitar a interface com o memcache. Outras bibliotecas ainda codificaram todo o processo descrito acima; Data :: ObjectDriver no Perl é apenas uma biblioteca. Quanto a outros idiomas, você precisará fazer sua própria pesquisa.
Espero que você tenha achado esta resposta útil. O que eu descobri com mais frequência é que a escalabilidade de um sistema se reduz cada vez menos ao código, e cada vez mais a uma boa estratégia de armazenamento e gerenciamento de dados / design técnico.