Aplicativo consultando tabelas vazias

10

Minha empresa usa um aplicativo que apresenta problemas de desempenho bastante importantes. Há vários problemas no banco de dados em que estou trabalhando, mas muitos deles são puramente relacionados ao aplicativo.

Na minha investigação, descobri que existem milhões de consultas no banco de dados do SQL Server que consultam tabelas vazias. Temos cerca de 300 tabelas vazias e algumas dessas tabelas são consultadas até 100-200 vezes por minuto. As tabelas não têm nada a ver com nossa área de negócios e são essencialmente partes do aplicativo original que o fornecedor não removeu quando elas foram contratadas pela minha empresa para produzir uma solução de software para nós.

Além do fato de suspeitarmos que nosso log de erros do aplicativo está sendo inundado por erros relacionados a esse problema, o fornecedor garante que não há impacto no desempenho ou na estabilidade do aplicativo ou do servidor de banco de dados. O log de erros é inundado na medida em que não podemos ver mais de 2 minutos de erros para fazer diagnósticos.

O custo real dessas consultas obviamente será baixo em termos de ciclos de CPU, etc. Mas alguém pode sugerir qual seria o efeito no SQL Server e no aplicativo? Eu suspeitaria que a mecânica real de enviar uma solicitação, confirmá-la, processá-la, devolvê-la e confirmar o recebimento pelo aplicativo teria um impacto no desempenho.

Usamos o SQL Server 2008 R2, Oracle Weblogic 11g para o aplicativo.

@ Frisbee- Para encurtar a história, criei uma tabela contendo o texto da consulta que atingiu as tabelas vazias no banco de dados do aplicativo e, em seguida, consultei todos os nomes de tabela que eu sei que estão vazios e recebi uma lista muito longa. O maior sucesso foi em 2,7 milhões de execuções em 30 dias de atividade, tendo em mente que o aplicativo geralmente está em uso das 8 às 18 horas, para que esses números sejam mais concentrados nas horas operacionais. Múltiplas tabelas, várias consultas, provavelmente algumas relavent via junções, outras não. O maior sucesso (2,7 milhões na época) foi uma simples seleção de uma única tabela vazia com uma cláusula where, sem junções. Eu esperaria que consultas maiores com junções às tabelas vazias incluíssem atualizações nas tabelas vinculadas, mas vou verificar isso e atualizar esta pergunta o mais rápido possível.

Atualização: existem 1000 consultas com uma contagem de execução entre 1043 - 4622614 (mais de 2,5 meses). Vou ter que cavar mais para descobrir quando o plano em cache se origina. Isso é apenas para lhe dar uma idéia da extensão das consultas. A maioria é razoavelmente complexa, com mais de 20 junções.

@ srutzky- sim, acredito que exista uma coluna de data relacionada a quando o plano foi compilado, para que seja de seu interesse, por isso vou verificar. Gostaria de saber se os limites de encadeamento seriam um fator quando o SQL Server estiver em um cluster VMware? Em breve será um Dell PE 730xD dedicado, felizmente.

@Frisbee - Desculpe pela resposta tardia. Como você sugeriu, eu executei um select * da tabela vazia 10.000 vezes em 24 threads usando o SQLQueryStress (na verdade, 240.000 iterações) e atingi 10.000 solicitações em lote / s imediatamente. Reduzi para 1000 vezes mais de 24 threads e atingi pouco menos de 4.000 solicitações em lote / s. Eu também tentei 10.000 iterações em apenas 12 threads (so 120000 iterações totais) e isso produziu 6.505 lotes / s sustentados. O efeito na CPU foi realmente perceptível, em torno de 5 a 10% do uso total da CPU durante cada execução de teste. As esperas na rede eram insignificantes (como 3ms com o cliente na minha estação de trabalho), mas o impacto na CPU estava lá, com certeza, o que é bastante conclusivo para mim. Parece resumir-se ao uso da CPU e um pouco de E / S desnecessária de arquivo de banco de dados. O total de execuções / segundo funciona em pouco menos de 3000, que é mais do que em produção, no entanto, estou testando apenas uma das dezenas de consultas como essa. O efeito líquido de centenas de consultas atingindo tabelas vazias a uma taxa entre 300-4000 vezes por minuto, portanto, não seria desprezível no que diz respeito ao tempo da CPU. Todos os testes foram feitos em um PE 730xD inativo com matriz de flash duplo e 256 GB de RAM, 12 núcleos modernos. Esta é a saída do SQLSentry

@ srutzky- bom pensamento. O SQLQueryStress parece usar o pool de conexões por padrão, mas eu dei uma olhada de qualquer maneira e descobri que sim, a caixa de pool de conexões está marcada. Atualize para seguir

@ srutzky- O pool de conexões aparentemente não está ativado no aplicativo - ou, se estiver, não está funcionando. Fiz um rastreamento do criador de perfil e descobri que as conexões têm EventSubClass "1 - Não em pool" para eventos de Logon de Auditoria.

RE: Pool de conexões - Verificou os weblogics e encontrou o pool de conexões ativado. Executou mais rastreamentos contra sinais ao vivo e encontrou que o pool não está ocorrendo corretamente / de modo algum: insira a descrição da imagem aqui

E aqui está o que parece quando executo uma única consulta sem junções em uma tabela preenchida; as exceções exibem "Ocorreu um erro relacionado à rede ou à instância ao estabelecer uma conexão com o SQL Server. O servidor não foi encontrado ou não estava acessível. Verifique se o nome da instância está correto e se o SQL Server está configurado para permitir conexões remotas. (provedor: provedor de pipes nomeados, erro: 40 - Não foi possível abrir uma conexão com o SQL Server) "Observe o contador de solicitações em lote. Executar ping no servidor durante o tempo em que as exceções são geradas resulta em uma resposta de ping bem-sucedida.

insira a descrição da imagem aqui

Atualização - duas execuções de teste consecutivas, mesma carga de trabalho (selecione * deEmptyTable), pool ativado / não ativado. Um pouco mais de uso da CPU e muitas falhas e nunca ultrapassa 500 solicitações em lote / s. Os testes mostram 10.000 lotes / s e nenhuma falha com o pool LIGADO, e cerca de 400 lotes / s, em seguida, muitas falhas devido à desativação do pool. Gostaria de saber se essas falhas estão relacionadas à falta de disponibilidade de conexão?

insira a descrição da imagem aqui

@ srutzky- Selecione Contagem (*) em sys.dm_exec_connections;

  • Pool ativado: 37 de forma consistente, mesmo após o teste de carga ser interrompido

  • Pool desabilitado: 11-37, dependendo da ocorrência ou não de exceções
    no SQLQueryStress, isto é: quando essas calhas aparecem no
    gráfico Lotes / s, as exceções ocorrem no SQLQueryStress e o
    número de conexões cai para 11 e, em seguida, volta gradualmente para 37 quando os lotes começam a atingir o pico e as exceções não estão ocorrendo. Muito, muito interessante.

O número máximo de conexões nas instâncias de teste / ao vivo é definido como o padrão 0.

Verificamos os logs do aplicativo e não conseguimos encontrar problemas de conectividade, no entanto, existem apenas alguns minutos disponíveis devido ao grande número e tamanho de erros, ou seja: muitos erros de rastreamento de pilha. Um colega no suporte a aplicativos recomenda que ocorra um número substancial de erros de HTTP relacionados à conectividade. Parece que, por algum motivo, o aplicativo não está agrupando corretamente as conexões e, como resultado, o servidor está ficando repetidamente sem conexões. Vou examinar mais os logs de aplicativos. Gostaria de saber se existe uma maneira de provar que isso está acontecendo na produção do lado do SQL Server?

@ srutzky- Obrigado. Amanhã vou verificar a configuração da weblogic e atualizar. Eu estava pensando sobre as meras 37 conexões - se SQLQueryStress está executando 12 threads em 10.000 iterações = 120.000 instruções de seleção sem pool, isso não significa que cada seleção cria uma conexão distinta com a instância sql?

@ srutzky- Weblogics estão configurados para agrupar conexões, então deve estar funcionando bem. O pool de conexões é configurado assim, em cada um dos 4 weblogics com balanceamento de carga:

  • Capacidade inicial: 10
  • Capacidade máxima: 50
  • Capacidade mínima: 5

Quando eu aumento o número de threads executando a consulta de seleção de tabela vazia, o número de conexões atinge um pico em torno de 47. Com o pool de conexões desabilitado, vejo consistentemente um número máximo de solicitações em lote / segundo mais baixo (de 10.000 para cerca de 400). O que acontece sempre é que as 'exceções' no SQLQueryStress ocorrem logo após os lotes / s entrarem em um vale. Está relacionado à conectividade, mas não consigo entender exatamente por que isso está acontecendo. Quando nenhum teste está sendo executado, #connections cai para cerca de 12.

Com o pool de conexões desabilitado, estou tendo problemas para entender por que as exceções ocorrem, mas talvez seja uma questão totalmente diferente de stackExchange / Adam Machanic?

@srutzky Gostaria de saber então por que as exceções ocorrem sem o pool ativado, mesmo que o SQL Server não esteja ficando sem conexões?

Peter
fonte
11
Peter, com as atualizações mais recentes em mente sobre o pool de conexões, parece que agora você precisa executar novamente seus testes com o SQLQueryStress, mas com o Connection Pooling desativado . Isso seria um reflexo mais preciso dos efeitos de como o aplicativo está funcionando e acredito que mostrará um aumento no uso da CPU e até no uso da RAM.
Solomon Rutzky
11
Peter, você tem um número máximo de conexões definidas para o servidor? Suponho que, sem o pool, você esteja enfrentando um problema de muitas conexões. Gostaria de saber se o seu aplicativo já recebeu esse erro. Além disso, se possível executar novamente o último teste mais uma vez (com e sem o pool ativado), enquanto o teste está sendo executado para cada uma dessas duas configurações, execute a SELECT COUNT(*) FROM sys.dm_exec_connections;para verificar se o valor é muito diferente entre ter o pool ativado ou não. Com base nesses erros, acho que haveria muito mais conexões quando o pool estiver desativado.
Solomon Rutzky
11
Peter, 37 conexões parece um máximo terrivelmente baixo. Dado que o limite de conexão está definido como 0 (ou seja, ilimitado), a memória do sistema está vinculada? Além disso, o pool de conexões deve estar ativado por padrão, mas é controlado pelo cliente. O aplicativo é um aplicativo .NET? Não precisa estar em ordem para usar o pool de conexões, mas ajudaria a saber para encontrar a causa disso. E você pode ver qual cadeia de conexão está sendo usada? Especifica Pooling=falseou Max Pool Size?
Solomon Rutzky
11
Peter, cada um dos 12 threads está criando sua própria conexão por consulta, sequencialmente para as 10k iterações. Portanto, sem pool, a conexão pode ser destruída assim que o código fechar a conexão. O pool manterá a conexão disponível para reutilização. Portanto, faz sentido que o número de conexões seja consistente ao usar o pool. Não sei por que 37 sem mais informações. Quantas conexões existem quando nenhum teste está sendo executado? Fazer o backup desse número fornecerá uma indicação melhor de quantos são criados pelo teste.
Solomon Rutzky
11
O pool de conexões é mantido por cliente, não por servidor. Portanto, o WebLogics e o SQLQueryStress devem ter seus próprios conjuntos de conexões (em termos dos tamanhos min_pool e max_pool, etc). Em relação a "Com o pool de conexões desativado, vejo um número mínimo de solicitações em lote por segundo": faz sentido, pois leva mais tempo para que cada conexão do aplicativo autentique e inicialize a sessão, etc. É exatamente por isso que existe o pool de conexões: - )
Solomon Rutzky

Respostas:

7

Eu suspeitaria que a mecânica real de enviar uma solicitação, confirmá-la, processá-la, devolvê-la e confirmar o recebimento pelo aplicativo teria um impacto no desempenho.

Sim, e existem até alguns fatores adicionais, mas é impossível afirmar em que grau algum deles está afetando seu sistema sem analisá-lo.

Dito isto, você está perguntando o que poderia ser um problema e há algumas coisas a serem mencionadas, mesmo que algumas delas não sejam atualmente um fator em sua situação específica. Você diz que:

Temos cerca de 300 tabelas vazias e algumas dessas tabelas são consultadas até 100-200 vezes por minuto.

  • Tabelas vazias que não estão sendo consultadas não são um problema. Mas acho que você também pode estar querendo dizer que todos estão sendo consultados, apenas que alguns estão sendo atingidos muito mais do que outros.
  • A análise de consultas e a geração do plano de execução não devem ser um grande problema se o texto da consulta enviado permanecer o mesmo nas chamadas. O SQL Server fará o hash do texto da consulta e procurará no cache do plano. Se encontrado, não executará as etapas de análise ou compilação novamente (até que o plano seja removido do cache).
  • Qualquer tabela, vazia ou não, exigirá pelo menos um bloqueio "compartilhado" para indicar que o recurso está sendo usado. Isso impede que operações que exijam bloqueios exclusivos (adicionar / alterar / remover colunas, etc.) façam a alteração enquanto o recurso estiver em uso. O bloqueio e desbloqueio, mesmo que sejam realizados em menos de 1 milissegundo, pois não há dados, ainda exigem recursos do sistema (memória e CPU) para gerenciar essas operações de bloqueio.
  • Mesmo sem conjuntos de resultados voltando ao aplicativo pelo SQL Server, ainda existe a mesma quantidade de tráfego de rede no SQL Server, independentemente de a consulta gerar resultados ou não. O texto da consulta ou o nome do procedimento armazenado precisa ser enviado. E mesmo que nenhum resultado volte, o SQL Server ainda precisará enviar alguns pacotes de rede que contêm a estrutura do conjunto de resultados, além de dizer ao cliente que um conjunto de resultados está sendo iniciado (mesmo que nenhuma linha seja encontrada) e que o conjunto de resultados será terminando e deve estar fechado. E pode haver mensagens adicionais de instruções impressas e / ou contagens de linhas.
  • A conexão ao SQL Server requer uma certa quantidade de recursos do sistema. É preciso CPU e memória para lidar com a autenticação (e também com os pacotes de rede) e isso também leva tempo. É por isso que o Pool de conexões existe: para reduzir essa despesa.
  • Mesmo com o Pool de conexões reduzindo o uso de recursos do sistema, o SQL Server ainda precisa manter essas conexões e isso requer memória e CPU mínima.
  • Mesmo sem linhas e, portanto, com um tempo de execução muito rápido, a consulta ainda era executada. Mesmo que houvesse 10 ou 10.000 linhas e essas fossem extraídas do Buffer Pool (ou seja, memória) desde que eram usadas com frequência, um encadeamento ainda precisa fazer esse trabalho. E um thread que está trabalhando nessa consulta inútil não está funcionando em uma consulta útil real.

Pode até haver mais, mas isso deve ajudar a entender as coisas. E lembre-se de que, como a maioria dos problemas de desempenho, é tudo uma questão de escala. Todos os itens mencionados acima não são problemas se forem atingidos uma vez por minuto. É como testar uma alteração em sua estação de trabalho ou no banco de dados de desenvolvimento: ele sempre funciona com apenas 10 - 100 linhas nas tabelas. Mova esse código para produção e leva 10 minutos para ser executado, e alguém é obrigado a dizer: "bem, funciona na minha caixa" ;-). Ou seja, é apenas devido ao grande volume de chamadas que você está vendo um problema, mas essa é a situação que existe.

Portanto, mesmo com 1 milhão de consultas inúteis, 0 linhas, isso equivale a:

  • um extra de 2 milhões de operações de bloqueio (cada bloqueio deve ser desbloqueado, certo?). isso é principalmente um custo de tempo gasto em uma operação inútil em vez de em uma operação útil.
  • mais tráfego de rede que pode estar mais perto da saturação (não tenho certeza da probabilidade, mas ainda assim)
  • mais conexões sendo mantidas que ocupam mais memória. Quanta RAM física não utilizada você possui? essa memória seria melhor usada para executar consultas e / ou cache do plano de consultas. Na pior das hipóteses, você está sem memória física e o SQL Server precisa começar a usar a memória virtual (swap), pois isso diminui a velocidade (verifique o log de erros do SQL Server para ver se você está recebendo mensagens sobre a paginação de memória).

    E apenas no caso de alguém mencionar "bem, existe um pool de conexões". Sim, isso definitivamente ajuda a reduzir o número de conexões necessárias. Porém, com consultas chegando até 200 vezes por minuto, ainda há muita atividade e conexões simultâneas para as solicitações legítimas. Faça um SELECT * FROM sys.dm_exec_connections;para ver quantas conexões ativas você está mantendo.

  • independentemente de qualquer outra coisa, isso ainda é pelo menos um milhão de vezes durante o dia em que um thread que poderia estar fazendo algo útil estava indisponível.

Se não estou incorreto sobre o que venho declarando aqui, parece-me que, mesmo em pequena escala, esse é um tipo de ataque DDoS ao seu sistema, pois está inundando a rede e o SQL Server com solicitações falsas. , impedindo que solicitações reais cheguem ao SQL Server ou sejam processadas pelo SQL Server.

Solomon Rutzky
fonte
1

Se as tabelas são atingidas 100-200 vezes por minuto, elas estão (espero) na memória. A carga no servidor é muito, muito baixa. A menos que você tenha alta CPU ou memória no servidor de banco de dados, isso provavelmente não é um problema.

Sim, as consultas usam bloqueios compartilhados, mas esperamos que não estejam bloqueando nenhum bloqueio de atualização nem bloqueados por nenhum bloqueio de atualização. Você tem alguma atualização, inserção ou exclusão nessas tabelas. Caso contrário, eu simplesmente deixaria para lá - se você está tendo problemas de desempenho, deve haver peixes maiores para fritar do ponto de vista do servidor de banco de dados.

Fiz um teste em 100.000 contagens de seleção (*) em uma tabela vazia e ela foi executada em 32 segundos e as consultas foram realizadas em uma rede. Então 1/3 milissegundo. A menos que sua rede esteja sobrecarregada, isso não afeta o cliente. Se você estiver tendo problemas importantes de desempenho, essas consultas em branco de 1/3 milissegundos não serão o que está matando o aplicativo.

E isso pode ser apenas parte de uma junção esquerda, capturando alguns dados estáticos do tipo que não faz parte do aplicativo atual. Pode ser encadeado com outras consultas, portanto não é uma viagem de ida e volta extra. Se sim, é desleixado, mas não está causando mais tráfego.

Então, voltemos a ver as declarações reais. Você está vendo alguma atualização, adição ou exclusão nessas tabelas?

Sim, muitas tabelas e consultas vazias para tabelas vazias são indicação de codificação incorreta. Mas se você estiver tendo problemas importantes de desempenho, essa não é a causa, a menos que você também tenha algumas operações de gravação realmente desleixadas nessas tabelas.

paparazzo
fonte
Quantos outros usuários estavam no SQL Server executando consultas quando você fez o teste de 100 mil consultas? Não estou dizendo que estou certo e que você está errado, mas se você fosse o único no sistema, ou um dos poucos, então naturalmente você não teria muito impacto. A questão do bloqueio não era uma questão de bloqueio, era apenas uma questão dos recursos necessários para o SQL Server bloquear e desbloquear essas páginas de dados, mesmo que elas estejam sempre no Buffer Pool. Ainda é trabalho que está sendo feito. E agendadores não são ilimitados.
Solomon Rutzky
E não estou dizendo que você está errado. Outros usuários ou não, ainda é uma medida válida de quanto tempo levou e uma medida de recursos. A carga declarada é de 100 a 200 por minuto. 100.000 de um cliente em 30 segundos excede essa carga em um fator de 200 a 400. Se não houver bloqueios de atualização, se vier de um cliente ou 100, não fará diferença. Sua resposta pressupõe que exista uma rede sobrecarregada ou um servidor SQL e com base na pergunta que você não conhece. Se fosse um ataque DDoS, haveria mais ou menos 100 / s (não minutos) e não seria contra uma tabela vazia.
Paparazzo
Correto, com base na pergunta que não sabemos o suficiente para reduzi-la, é por isso que eu estava dizendo que essas coisas poderiam ser um problema, dependendo das circunstâncias. E a questão do DDoS era apenas uma analogia, baseada principalmente na redação da pergunta original, o que implicava que vários foram atingidos nesse ritmo e muitos outros também, apenas com menos frequência.
Solomon Rutzky
Considero que essa é uma resposta valiosa no sentido de que o primeiro parágrafo resume muito bem: "A menos que você tenha alta CPU ou memória no servidor de banco de dados, isso provavelmente não é um problema". No nosso caso, temos alto uso de CPU em determinados horários do dia e, portanto, a pressão extra da CPU parece ser um fator com base nos meus testes.
Peter
Notavelmente, citei apenas consultas executando de 100 a 200 vezes / minuto, quando, na realidade, existem cerca de 50 consultas nessas tabelas vazias com contagens de execução entre 200-4000 / minuto. Cumulativamente, o efeito de consultar tabelas vazias com essa frequência afeta bastante a CPU, mesmo no melhor caso de consultas não parametrizadas executadas repetidamente, para que o plano, os dados etc. estejam todos na memória.
Peter Peter
0

Em geral, em cada consulta, são executadas as seguintes etapas:

  1. Solicitação do aplicativo.
  2. Banco de dados Analise a consulta.
  3. Mecanismo de banco de dados, verifique se esta consulta já está armazenada na RAM. use plano de execução se existir na memória.
  4. se não existir na RAM, o mecanismo de banco de dados verifica as estatísticas existentes nos objetos na consulta e determina o plano de execução.
  5. Execute o plano de execução, use a E / S para obter dados do disco.
  6. resposta à aplicação.

muitas consultas mencionadas podem causar carga extra em um sistema que já é pesado - carga extra em conexões, CPU, RAM e E / S.

alonk
fonte