O que qualifica "muitas solicitações de banco de dados" no código?

17

Esta é uma discussão que eu e alguns de meus colegas estamos tendo e pensamos que eu viria aqui e veria se havia um consenso geral sobre isso.

Basicamente, resume-se às duas opiniões a seguir sobre chamadas ao banco de dados: 1. Faça uma chamada grande para obter tudo o que for necessário para reduzir o número de chamadas ao banco de dados 2. Faça chamadas separadas menores com base no que é solicitado para reduzir o tamanho de Chamadas de banco de dados

Onde isso entra especialmente em jogo é no código comum. Usaremos o exemplo de uma classe Employee, pois isso é bastante direto.

Digamos que sua classe Employee tenha 10 atributos de valor (nome, sobrenome, contratado, etc.) e depois 2 atributos de classe ... 1 apontando para uma classe Department e depois 1 supervisor que aponta para outro objeto Employee.

Na mentalidade nº 1, você faria uma chamada que retornasse os dados do funcionário, bem como os campos necessários para preencher os atributos de departamento e supervisor ... ou, pelo menos, os campos mais usados ​​nesses subobjetos.

Na mentalidade nº 2, você preencheria apenas o objeto Employee primeiro e, em seguida, preencheria apenas os objetos Department e Supervisor se e quando forem realmente solicitados.

A postura do 2 é bastante direta ... minimize o tamanho das solicitações e quantos objetos de banco de dados precisam ser atingidos cada vez que uma dessas solicitações é feita. A posição 1 é que, mesmo que pudesse ser implementado corretamente, o simples fato de o código ter que fazer várias conexões causará mais pressão na conexão entre o servidor da Web e o banco de dados, em vez de reduzi-lo.

A força motriz por trás disso é que a quantidade de tráfego entre nosso servidor da web e o servidor de banco de dados está ficando fora de controle.

user107775
fonte
7
Na minha experiência, não há uma "resposta certa" para isso. Há um equilíbrio entre latência e taxa de transferência. A baixa latência pode tolerar muitos pedidos pequenos ou até um grande; no entanto, links de alta latência tendem a ser melhores ao mover muitos dados de uma só vez. No entanto, se a taxa de transferência for baixa em uma configuração de alta latência, é melhor buscar trechos menores para responder melhor.
3
Provavelmente relacionado ao problema n + 1 stackoverflow.com/questions/97197/…
Valera Kolupaev 26/09/11
@Valera: por conveniência aqui está o link postado sobre essa questão: realsolve.co.uk/site/tech/hib-tip-pitfall.php?name=n1selects
rwong
4
"a quantidade de tráfego entre nosso servidor web e o servidor de banco de dados está ficando fora de controle." O que isso significa? Você pode ser específico sobre qual é o verdadeiro problema? Você tem problemas de desempenho? Você já fez perfis e medições? Forneça os resultados reais das medições reais como parte da pergunta. Caso contrário, estamos apenas adivinhando.
S.Lott

Respostas:

8

Se a força motriz por trás dessa pergunta é muito tráfego, você examinou o cache de objetos usados ​​com frequência? Por exemplo: Depois de obter os objetos Employee, Department e Supervisor, talvez seja uma boa ideia adicioná-los em cache, para que, se forem solicitados novamente em um futuro próximo, eles já estejam em cache e não precisem ser recuperados. novamente. Obviamente, o cache precisará expirar e os objetos raramente usados ​​expirarão, além de poder remover objetos que foram modificados pelo aplicativo e salvos no banco de dados.

Dependendo do idioma e das estruturas que você estiver usando, talvez já exista uma estrutura de cache que possa fazer parte (ou a maioria) do que você precisa. Se você usa Java, pode procurar no Apache Commons-Cache (eu não o uso há algum tempo e, embora pareça inativo, ele ainda está disponível para uso e foi bastante decente na última vez em que o usei).

FrustratedWithFormsDesigner
fonte
3

Sempre busque legibilidade e clareza na primeira vez que escrever algo. Você pode refatorar se e quando precisar. Faça testes de carga para encontrar os gargalos; em muitos casos, não é o número de chamadas que causam o problema, mas as que são mal escritas.

Quanto ao que classifica como muitos, isso depende da aplicação. Para a maioria dos aplicativos da Web, qualquer coisa abaixo de 30 segundos é praticamente aceitável. Eu falaria com seus usuários sobre suas expectativas.

Tom Squires
fonte
O que constitui uma chamada db mal escrita?
nu everest
3

Sua pergunta parece baseada no pressuposto de que você precisa adivinhar quais dados serão necessários para qualquer página. Esse não é o caso. Não é tão fácil quanto a abordagem ingênua, mas você pode arquitetar seu código para saber se precisará de atributos de departamento ou supervisor antes de fazer qualquer chamada ao banco de dados.

Karl Bielefeldt
fonte
3

Essas são as regras que eu uso, talvez sejam úteis para você.

  1. Meça primeiro! Eu nem vou olhar para o código que "pode ​​ser lento", a menos que eu possa realmente ver o tráfego fluindo para esse recurso e esse recurso esteja respondendo lentamente.
  2. 1 Solicitação = K consultas. O número de vezes que converso com o banco de dados é totalmente determinado pelo tipo de recurso solicitado; e nunca pela natureza da solicitação ou estado desse recurso; No seu exemplo, é provável que haja no máximo três consultas: 1 para funcionários, 1 para departamentos e 1 para supervisores; Não importa quantos de cada um existe.
  3. Não pergunte o que você não usará . Se estamos falando de HTTP, não faz sentido consultar dados para mais tarde; não há mais tarde; cada solicitação começa em uma lista limpa. Às vezes, preciso da maioria das colunas de uma tabela, mas às vezes preciso apenas de uma ou duas; quando souber exatamente os campos de que preciso, pedirei exatamente isso.
  4. Jogue o hardware no problema. Servidores são baratos; Às vezes, você pode obter desempenho suficiente apenas movendo o banco de dados para uma caixa mais robusta; ou enviando algumas consultas para uma réplica somente leitura.
  5. Primeiro invalide o cache, depois implemente o cache. O desejo de colocar dados usados ​​ou difíceis de consultar em um cache é forte; mas, com muita frequência, a remoção de dados não utilizados ou a substituição de dados substituídos é ignorada. Se você souber retirar dados do cache; então você está seguro em colocá-lo no cache; Se for mais caro invalidar o cache do que apenas fazer a consulta; então você não precisava de um cache.
SingleNegationElimination
fonte
2

Ambas as estratégias aqui são perfeitamente válidas. Há vantagens e desvantagens para cada um:

Uma chamada para todos os 3 objetos:

  • irá executar mais rápido
  • você terá exatamente o que precisa no caso em que precisa
  • provavelmente só será utilizável em um caso (pode ser um caso muito comum)
  • será mais difícil de manter
  • precisará ser mantido com mais frequência (pois será alterado se algum dos esquemas dos 3 objetos ou os dados necessários forem alterados)

Uma chamada por objeto (total de 3 chamadas)

  • Oferece uma chamada de uso geral para preencher uma única instância de cada tipo de objeto; eles podem ser usados ​​independentemente
  • Será mais sustentável, pois a estrutura da consulta será mais simples.
  • Será mais lento (não necessariamente três vezes mais lento, mas a sobrecarga é aumentada para os mesmos dados)
  • Pode causar problemas na recuperação de dados desnecessários (puxar o registro inteiro quando você precisar de um campo é um desperdício)
  • Pode causar problemas de N + 1 quando existe um relacionamento muitos-para-um, se a consulta de registro único for enviada N vezes, uma por registro na coleção.
KeithS
fonte
Em resposta a algumas de suas preocupações (nºs 3 e 5 da segunda lista) ... E se o Supervisor e o Departamento forem usados ​​apenas 1/3 (ou menos) do tempo? E se o código fosse projetado para obter todos os filhos assim que o objeto List <> codificado para contê-los fosse mencionado pela primeira vez? ... isso aliviaria a maior parte da cautela?
user107775
Se os objetos auxiliares raramente forem necessários, em geral, o desempenho será mais rápido (menos dados a serem recuperados), mas o pior caso será mais lento (mesmos dados ou mais recuperados, usando três vezes a sobrecarga de comunicação do computador). Quanto ao problema N + 1, você simplesmente precisa ser capaz de arquitetar a consulta que recupera uma lista de objetos para poder fazer isso com base na chave estrangeira do lado "um" do relacionamento e, em seguida, puxar várias linhas fora do resultado da consulta. Você não pode usar uma versão da consulta que precisa ter a chave primária do registro.
Keith
1

Para mim, muitas solicitações de banco de dados estão fazendo mais solicitações do que você precisa para carregar os dados necessários a qualquer momento.

Então, você não precisa dos dados, não desperdice memória para evitar uma segunda viagem mais tarde. Mas se você precisar da quantidade de dados, minimize as chamadas para o banco de dados.

Portanto, tenha as duas opções e use cada uma onde a situação exigir.

EDIT: Lembre-se de que esse curso também depende da sua situação. Se for um WebApp, por exemplo, você deve ter considerações diferentes do que se for um aplicativo de desktop acessando o banco de dados na sua rede, em vez de na Web o WepApp.

AJC
fonte
E se você estiver escrevendo um código comum e não tiver certeza da maneira como seu código será usado. Talvez você nunca imagine alguém que não precise do supervisor, mas o aplicativo em que você trabalha é o único que precisa. Claro, você poderia escrever funções separadas ... uma para não incluí-la e outra para incluí-la, mas em que momento seu código comum começa a exigir muito conhecimento detalhado para poder usar?
user107775
@ user107775 Normalmente, escrevo apenas duas funções para cada caso; um que retorna apenas os valores da propriedade e outro que retorna a classe com todas as classes relacionadas. Isso ocorre porque, na maioria das vezes, você só precisa das propriedades. Dessa forma, você não precisa de conhecimentos detalhados, apenas um recebe o básico e o outro tudo. Acho que é um equilíbrio razoável. (No entanto, alguns casos específicos exigem mais otimização, mas isso é caso a caso).
AJC
1

Conecte-se ao banco de dados, envie a solicitação e a analise normalmente leva um tempo significativo em comparação à recuperação dos resultados, portanto, a tendência geral é concatenar o maior número possível de consultas em uma solicitação.

Ainda assim, fazer tudo de uma só vez tornará o código insustentável. Em vez disso, geralmente é atingido por uma camada de abstração adicional: o código agenda várias solicitações conforme necessário, o mecanismo analisa isso como uma grande solicitação (possivelmente usando cache no caminho) e as respostas são enviadas conforme necessário.

É claro que nem sempre tudo pode ser recuperado em uma consulta - geralmente você terá uma consulta que fornece os dados necessários para a construção da próxima consulta, portanto, você precisará repeti-la. Ainda assim, pacotes impressionantes de consultas e a execução do maior número possível de uma só vez são melhores do que centenas de pequenas tentativas no banco de dados.

Portanto, planeje o que você precisa, solicite e recupere, se mais for necessário, solicite e recupere novamente e, em seguida, utilize os dados na geração de conteúdo. Definitivamente, evite usar solicitações de banco de dados, como inicialização de variável local espalhada por todo o código.

SF.
fonte
1

Não sabemos o suficiente sobre o seu aplicativo para saber qual opção você é culpada de otimizar muito cedo. Com que frequência os dados do Supervisor são utilizados? Parece que pode ser um desperdício, mas não sabemos. Se você os mantiver separados, poderá monitorar seu sistema para ver com que frequência eles acabam sendo usados ​​juntos. Você pode decidir apenas combiná-los em uma ligação. Caso contrário, se você começar a criar um gargalo com essa grande ligação, por onde começar a solucionar problemas? Difícil identificar o que faz sentido omitir. Mais campos de dados podem ser adicionados a esse processo.

Seria interessante saber quanto disso vem da memória db vs disco. Não há nada que me faça sentir que é mais ou menos provável que o departamento mude em relação ao endereço.

JeffO
fonte