As chamadas múltiplas de banco de dados são realmente significativas com uma chamada de rede para uma API da web?

16

Em um de meus empregadores, trabalhamos em uma API REST (mas também se aplica ao SOAP). O cliente, que é a interface do usuário do aplicativo, faria chamadas pela Web (LAN em implantações típicas de produção) para a API. A API faria chamadas para o banco de dados.

Um tema recorrente nas nossas discussões é o desempenho: algumas pessoas da equipe acreditam que você não deve ter várias chamadas ao banco de dados (geralmente lê) de uma única chamada da API por causa do desempenho; você deve otimizá-los para que cada chamada à API tenha apenas (exatamente) uma chamada ao banco de dados.

Mas isso é realmente importante? Considere que a interface do usuário precisa fazer uma chamada de rede para a API; isso é muito grande (ordem de magnitude de milissegundos). Os bancos de dados são otimizados para manter as coisas na memória e executar leituras muito, muito rapidamente (por exemplo, o SQL Server carrega e mantém tudo na RAM e consome quase toda a RAM livre, se puder).

TLDR: É realmente significativo se preocupar com várias chamadas ao banco de dados quando já estamos fazendo uma chamada de rede pela LAN? Se sim, por quê?

Para ser claro, estou falando de ordem de magnitude - eu sei que depende de detalhes (hardware da máquina, escolha de API e DB, etc.) Se eu tiver uma chamada que leva O (milissegundos), otimiza para DB chamadas que levam uma ordem de magnitude menor, realmente importam? Ou há mais para o problema do que isso?

Edit: para a posteridade, acho ridículo afirmar que precisamos melhorar o desempenho combinando chamadas de banco de dados nessas circunstâncias - especialmente com a falta de criação de perfil. No entanto, não é minha decisão se fazemos isso ou não; Quero saber qual é a lógica por trás de pensar que essa é uma maneira correta de otimizar as chamadas de API da web.

ashes999
fonte
Não há outra chamada de rede entre a camada da API e o banco de dados?
Sinal
4
O que seus testes de tempo mostraram?
Dan Pichelman
@Sign Não há chamada de rede entre a API e o banco de dados. Eles estão garantidos na mesma máquina, pelo que entendi.
precisa saber é o seguinte
@DanPichelman é o que estou perguntando também. Ninguém parece estar tendo desempenho e tempo; nós apenas obtemos requisitos para "corrigir o desempenho no X combinando todas as chamadas de banco de dados em uma única chamada".
precisa saber é o seguinte

Respostas:

25

Mas isso é realmente importante? Considere que a interface do usuário precisa fazer uma chamada de rede para a API; isso é muito grande (ordem de magnitude de milissegundos). Os bancos de dados são otimizados para manter as coisas na memória e executar leituras muito, muito rapidamente (por exemplo, o SQL Server carrega e mantém tudo na RAM e consome quase toda a RAM livre, se puder).

A lógica

Em teoria, você está correto. No entanto, existem algumas falhas nessa lógica:

  1. Pelo que você declarou, não está claro se você realmente testou / definiu o perfil do seu aplicativo. Em outras palavras, você realmente sabe que as transferências de rede do aplicativo para a API são o componente mais lento? Por ser intuitivo, é fácil supor que sim. No entanto, ao discutir desempenho, você nunca deve assumir. No meu empregador, sou o líder de desempenho. Quando eu entrei, as pessoas continuaram falando sobre CDNs, replicação etc. com base na intuição sobre quais devem ser os gargalos. Acontece que nossos maiores problemas de desempenho foram o desempenho ruim das consultas ao banco de dados.

  2. Você está dizendo que, como os bancos de dados são bons na recuperação de dados, o banco de dados está necessariamente em execução com desempenho máximo, está sendo usado de maneira ideal e não há nada que possa ser feito para melhorá-los. Em outras palavras, os bancos de dados são projetados para serem rápidos, portanto nunca precisarei me preocupar com isso. Outra linha de pensamento perigosa. É como dizer que um carro deve se mover rapidamente, então não preciso trocar o óleo.

  3. Essa maneira de pensar assume um único processo de cada vez, ou, em outras palavras, nenhuma simultaneidade. Ele pressupõe que uma solicitação não possa influenciar o desempenho de outra solicitação. Os recursos são compartilhados, como E / S de disco, largura de banda de rede, conjuntos de conexões, memória, ciclos de CPU, etc. Portanto, reduzir o uso de um recurso compartilhado por uma chamada de banco de dados pode impedir que outras solicitações diminuam a velocidade. Quando entrei para meu empregador atual, a gerência acreditava que ajustar uma consulta ao banco de dados de 3 segundos era uma perda de tempo. 3 segundos é tão pouco, por que perder tempo com isso? Não estaríamos melhor com uma CDN ou compactação ou outra coisa? Mas se eu puder executar uma consulta de 3 segundos em 1 segundo, digamos, adicionando um índice, que é 2/3 a menos de bloqueio, 2/3 a menos de tempo gasto ocupando um encadeamento e, mais importante, menos dados lidos no disco,

A teoria

Existe uma concepção comum de que o desempenho do software é simplesmente velocidade .

De uma perspectiva puramente de velocidade, você está certo. Um sistema é tão rápido quanto seu componente mais lento. Se você definiu o perfil do seu código e descobriu que a Internet é o componente mais lento, obviamente todo o resto não é a parte mais lenta.

No entanto, considerando o exposto, espero que você possa ver como a contenção de recursos, a falta de indexação, o código mal escrito etc. podem criar diferenças surpreendentes no desempenho.

As suposições

Uma última coisa. Você mencionou que uma chamada de banco de dados deve ser barata em comparação com uma chamada de rede do aplicativo para a API. Mas você também mencionou que o aplicativo e os servidores de API estão na mesma LAN. Portanto, os dois não são comparáveis ​​às chamadas de rede? Em outras palavras, por que você está assumindo que a transferência da API é uma ordem de magnitude mais lenta que a transferência do banco de dados quando ambos têm a mesma largura de banda disponível? É claro que os protocolos e as estruturas de dados são diferentes, entendo isso, mas discuto a suposição de que são ordens de magnitude diferentes.

Onde fica murkey

Essa questão toda é sobre chamadas de banco de dados "múltiplas" versus "únicas". Mas não está claro quantas são múltiplas. Por causa do que eu disse acima, como regra geral, recomendo fazer quantas chamadas de banco de dados forem necessárias. Mas isso é apenas uma regra de ouro.

Aqui está o porquê:

  1. Os bancos de dados são ótimos na leitura de dados. Eles são mecanismos de armazenamento. No entanto, sua lógica de negócios reside em seu aplicativo. Se você estabelecer uma regra de que toda chamada de API resulta em exatamente uma chamada de banco de dados, sua lógica de negócios pode acabar no banco de dados. Talvez esteja tudo bem. Muitos sistemas fazem isso. Mas alguns não. É sobre flexibilidade.
  2. Às vezes, para obter uma boa dissociação, você deseja separar duas chamadas de banco de dados. Por exemplo, talvez toda solicitação HTTP seja roteada através de um filtro de segurança genérico que valide do banco de dados que o usuário tem os direitos de acesso corretos. Se o fizerem, continue executando a função apropriada para esse URL. Essa função pode interagir com o banco de dados.
  3. Chamando o banco de dados em um loop. Foi por isso que perguntei quantos são múltiplos. No exemplo acima, você teria 2 chamadas ao banco de dados. 2 está bem. 3 pode estar bem. N não está bem. Se você chamar o banco de dados em um loop, agora tornou o desempenho linear, o que significa que levará mais tempo quanto mais estiver na entrada do loop. Dizer categoricamente que o tempo de rede da API é o mais lento ignora completamente anomalias como 1% do seu tráfego, demorando muito tempo devido a um loop ainda não descoberto que chama o banco de dados 10.000 vezes.
  4. Às vezes, há coisas em que seu aplicativo é melhor, como alguns cálculos complexos. Pode ser necessário ler alguns dados do banco de dados, fazer alguns cálculos e, com base nos resultados, passar um parâmetro para uma segunda chamada de banco de dados (talvez para gravar alguns resultados). Se você combiná-las em uma única chamada (como um procedimento armazenado) apenas para chamar o banco de dados apenas uma vez, forçou-se a usar o banco de dados para algo em que o servidor de aplicativos possa ser melhor.
  5. Balanceamento de carga: você possui 1 banco de dados (presumivelmente) e vários servidores de aplicativos com balanceamento de carga. Portanto, quanto mais trabalho o aplicativo faz e menos banco de dados, mais fácil é dimensionar, porque geralmente é mais fácil adicionar um servidor de aplicativos do que configurar a replicação do banco de dados. Com base no marcador anterior, pode fazer sentido executar uma consulta SQL e, em seguida, fazer todos os cálculos no aplicativo, que é distribuído por vários servidores, e gravar os resultados quando terminar. Isso poderia proporcionar melhor taxa de transferência (mesmo que o tempo total da transação seja o mesmo).

TL; DR

TLDR: É realmente significativo se preocupar com várias chamadas ao banco de dados quando já estamos fazendo uma chamada de rede pela LAN? Se sim, por quê?

Sim, mas apenas até certo ponto. Você deve tentar minimizar o número de chamadas ao banco de dados quando for prático, mas não combine chamadas que não tenham nada a ver apenas com o objetivo de combiná-las. Além disso, evite chamar o banco de dados em um loop a todo custo.

Brandon
fonte
3

Parece que sua equipe está otimizando antes de ter um motivo. Você mediu o tempo para executar essas solicitações? As chances de forçar esse paradigma criarão um desempenho pior para o usuário final, pois as viagens de ida e volta ao servidor da Web terão uma latência muito maior do que o tempo de conexão do servidor da Web ao banco de dados. Além disso, a maioria dos navegadores da Web fará apenas duas conexões simultâneas com um único servidor da Web; portanto, para páginas complexas, você provavelmente terá um gargalo.

De qualquer maneira, as decisões de otimização não devem ser tomadas sem dados para fazer backup. Meça e descubra o que é melhor para a sua aplicação.

brianfeucht
fonte
1
Este é um bom comentário sobre nossas práticas inadequadas de desempenho, mas não responde à minha pergunta sobre se as chamadas de banco de dados são algo para se preocupar quando eu já tiver uma chamada de rede.
precisa saber é o seguinte
1
Em geral, descobri que fazer várias chamadas ao banco de dados não é um problema. Isso ocorre principalmente devido ao pool de conexões e à pequena latência entre o banco de dados e o servidor da web. Há um ponto em que fazer várias chamadas de banco de dados diferentes afetará negativamente o desempenho, mas eu não tenho um número fixo para você. Tudo depende do ambiente e do aplicativo. Somente a medição fornecerá a resposta que você procura.
brianfeucht
Não deve (necessariamente) depender de detalhes, porque estou falando de ordem de magnitude.
precisa saber é o seguinte
Apenas suposições (você precisa medir): Tempo médio para conectar-se ao banco de dados do servidor da Web: 2ms Tempo médio para conectar-se ao servidor da Web do cliente: 20ms Portanto, assumindo que os números que eu retirei aleatoriamente do ar estão corretos, você poderia fazer 10 chamadas de banco de dados no tempo necessário para realizar uma chamada de serviço da web. Supondo que as consultas ao banco de dados demorem a mesma quantidade de tempo. Esses números são extremamente dependentes do ambiente. Se o cliente que está fazendo a chamada de serviço da Web for local, isso poderá ser reduzido em várias ordens de magnitude.
brianfeucht
2

Nós não podemos contar.

Não temos a aparência de suas consultas. Não sabemos quanto tempo eles levam para serem concluídos. Não sabemos quanta sobrecarga está envolvida em cada solicitação ao seu servidor de API. Não sabemos como seus clientes estão geograficamente dispersos. Etc.

Se esse é um cenário que requer otimização e você pode decidir se deseja dividir ou unir as chamadas, é necessário compará-lo de duas maneiras : decida o que você está otimizando (latência da interface do usuário, carga da CPU do servidor, contenção, etc.) e escolha aquele que melhor atingir sua meta de otimização.


Afora isso, a única uma coisa que posso acrescentar com relativa certeza é esta:

Dentro de uma única solicitação, você deve executar todas as consultas necessárias para criar uma resposta.

Em outras palavras, se a resposta não puder ser gerada até que todas as N consultas sejam executadas, geralmente não faz sentido separá-las. Se você pode gerar resultados significativos, intermediários ou completos, após cada consulta, inicie o benchmarking.

svidgen
fonte
1

Dois pensamentos:

Primeiro, para o consumidor que usa a API, ele está fazendo uma ligação para realizar uma tarefa. O que acontece depois que o servidor recebe a chamada para atender à solicitação não deve ser tão rígido. Se essa chamada de um consumidor exigir 10 itens de sub-trabalho para reunir os dados e devolvê-los, isso deve ser aceitável.

Segundo: você vê um problema real de desempenho do banco de dados com o processo em questão? Minha experiência mostrou que, muitas vezes, tentar colocar todos os aspectos de uma solicitação de banco de dados em uma única chamada pode resultar em uma chamada menos eficiente do que simplesmente fazer três ou quatro chamadas de dados. Os bancos de dados modernos são muito eficientes nos planos de cache e execução. Frequentemente, quando você tenta fazer demais, verá procedimentos com cursores (muito ruins para o desempenho porque os dados são atuados linha por linha, não como um conjunto de uma vez) e código que resulta em um plano menos eficiente do que se você tivesse quebrado a chamada em várias pequenas etapas fáceis.

Fora da organização simples do código, concordo que cada chamada de API possivelmente chame um único procedimento armazenado (ou função db), que por sua vez é responsável por preencher a solicitação. Pode haver mais de uma etapa no procedimento.

Richard
fonte
Concordo com você sobre como medir o desempenho, o que ninguém parece estar fazendo. Não há provas de que isso seja mais rápido, mas continua aparecendo. O desempenho surge como um problema quando temos algumas chamadas que podem fazer, por exemplo, 1000 DB SELECTs.
precisa saber é o seguinte
@ ashes999, embora você possa ganhar velocidade observando o número de chamadas em banco de dados, é mais provável que seja encontrado na estratégia de indexação etc., e não no número de chamadas. Como todos indicaram, observe os dados de desempenho.
Richard
Richard, eu concordo, e eu realmente sei disso. Minha pergunta é por que várias pessoas continuam abordando esse ponto em que "várias chamadas de banco de dados são lentas" quando há uma chamada de rede envolvida. Realmente não vejo como isso pode ser significativo.
precisa saber é o seguinte
@ ashes999 Desculpe, talvez você deva entrar em mais detalhes sobre a chamada de rede, já que isso parece óbvio, acho que há um pouco mais na sua pergunta. Sinto que falta algo em suas perguntas. Você sempre sofrerá com alguma latência da rede e cada chamada potencialmente aumenta em "x" vezes para cada chamada (em termos simples). A afirmação no valor nominal é verdadeira, várias chamadas de rede serão mais lentas que uma chamada de rede para o banco de dados. É por isso que sugiro uma chamada para um procedimento armazenado, que pode fazer várias chamadas para o banco de dados sem as chamadas de rede múltipla.
Richard
1

Se o banco de dados estiver em um servidor diferente do seu serviço REST, cada chamada ao banco de dados resultará em uma ida e volta da rede e isso poderá prejudicar significativamente o desempenho:

Certa vez, observei que uma única chamada de serviço da web era traduzida para cerca de 500 consultas de banco de dados - isso dificilmente era um problema quando o serviço da web e o banco de dados estavam localizados na mesma máquina, mas se transformavam em um tempo de resposta de 6 a 7 segundos quando eles estavam em diferentes máquinas

Obviamente, 500 viagens de ida e volta ao banco de dados são bastante extremas. Não sei ao certo quais são seus requisitos de desempenho, mas como regra geral, eu diria que, se você ficar com menos de 10 consultas ao banco de dados por chamada REST, não deverá sofrer um impacto significativo no desempenho.

Astrotrain
fonte
1

Temos alguns aplicativos muito, muito faladores. Há uma chamada de banco de dados para todos. Solteiro. Pequeno. Coisa. Servir dados de referência repetidamente é uma parte importante da carga de trabalho no sistema. Todo esse agendamento de threads de trabalho, aquisição e remoção de bloqueios, planejamento de verificação de cache etc. se soma, mesmo que não haja E / S de disco real. A contenção é maior porque as transações precisam reter bloqueios em várias chamadas de banco de dados e, portanto, a taxa de transferência é muito menor do que poderia ser. Agora, essas equipes estão tentando comprar servidores de banco de dados novos e muito caros por causa disso.

Portanto, embora a maior parte do tempo decorrido na configuração atual do sistema seja gasto com chamadas da API REST, ignorar o desempenho no nível do banco de dados está armazenando problemas para o futuro.

Michael Green
fonte
0

O caminho de otimização apresentado é simplesmente a maneira errada de ver as coisas.

As chamadas de API devem ser atômicas. Em outras palavras, eu devo poder fazer uma chamada de API da web para executar a ação que eu quero. Seja para buscar dados, atualize um registro ou o que for. NUNCA deve levar mais de uma chamada para causar a ação. E a tentativa de alavancar transações em várias chamadas deve ser evitada como uma praga.

Às vezes, uma única ação é bastante complexa. Por exemplo, buscando dados combinados de várias fontes: novamente, isso deve ser uma única chamada. Ou a coisa toda funciona ou a coisa toda falha.

Agora, dizer que uma única chamada de API deve executar apenas uma consulta ao banco de dados é um pouco idiota. Como você apontou, a sobrecarga para organizar a chamada pela rede geralmente é uma ordem de magnitude mais cara em termos de tempo geral.

Eu posso entender um pouco a afirmação de que uma única consulta é executada pode ser mais rápida que várias; mas isso dá uma impressão falsa, pois ignora o total de dados e carga de rede. Somente analisando as várias maneiras de extrair dados do banco de dados você pode descobrir qual é realmente o problema. Tenho certeza de que todo mundo tem uma história em que uma consulta específica executada 100 vezes mais frequentemente do que o esperado matou o sistema até que um índice adequado fosse implementado ...

Em última análise, você não será capaz de convencê-los com apenas uma conversa. Configure um caso de teste para as duas abordagens e faça o perfil delas. Preste atenção ao tempo total para adquirir os dados necessários, quantidade de tráfego de rede gerado, número e tempo das chamadas ao banco de dados etc. dados para comer corvo ou mostrar o caminho dourado.

Eu não
fonte