AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 153159
Accepted
Ivan McA
Ivan McA
Asked: 2016-10-25 00:59:24 +0800 CST2016-10-25 00:59:24 +0800 CST 2016-10-25 00:59:24 +0800 CST

Otimizando o SQL Server para um aplicativo que acessa repetidamente o banco de dados com exatamente a mesma consulta?

  • 772

Estou lidando com problemas de desempenho com um aplicativo do Windows que usa o SQL Server Express (2014) no back-end.

Consegui fazer isso funcionar muito melhor, principalmente revisando o lado da indexação do SQL Server, mas há um relatório específico que ainda está sendo executado muito lentamente.

Olhando para o que está fazendo, parece estar fazendo um loop no aplicativo e consultando milhares de SELECT *consultas muito simples em uma tabela WHERE = Primary Key, recuperando apenas um registro em cada caso. E quando digo IDÊNTICO, quero dizer idêntico, não está nem variando a chave primária para obter coisas diferentes, está pedindo exatamente o mesmo registro de volta do banco de dados aparentemente cada vez que precisa, até cem vezes em apenas alguns segundos.

Este é um relatório de exemplo que leva cerca de 10 a 15 segundos para ser executado quando o servidor está silencioso - quantas vezes a consulta é executada, adicionei como um comentário:

SELECT * FROM "Patient" WHERE "_Recno" = 35051  -- (runs 106 times)
SELECT * FROM "Client" WHERE "_Recno" = 15607   -- (99 times)
SELECT * FROM "SpeciesEntry" WHERE "_Recno" = 180   -- (97)
SELECT * FROM "Table" WHERE "_Recno" = 9    -- (97)
SELECT * FROM "DefaultEntry" WHERE "_Recno" = 2615  -- (96)
SELECT * FROM "Table" WHERE "_Recno" = 34   -- (96)
SELECT * FROM "DefaultEntry" WHERE "_Recno" = 2562  -- (84)
SELECT * FROM "Table" WHERE "_Recno" = 33   -- (84)
SELECT * FROM "Treatment" WHERE "_Recno" = 1682 -- (33)
SELECT * FROM "Treatment" WHERE "_Recno" = 1819 -- (33)
SELECT * FROM "Treatment" WHERE "_Recno" = 927  -- (33)
SELECT * FROM "Treatment" WHERE "_Recno" = 934  -- (33)
SELECT * FROM "Treatment" WHERE "_Recno" = 935  -- (33)
SELECT * FROM "Treatment" WHERE "_Recno" = 940  -- (33)
SELECT * FROM "Treatment" WHERE "_Recno" = 942  -- (33)
SELECT * FROM "Treatment" WHERE "_Recno" = 944  -- (33)
SELECT * FROM "OptionWP" WHERE "_Recno" = 103   -- (3)
SELECT * FROM "OptionWP" WHERE "_Recno" = 54    -- (1)
SELECT * FROM "PatientEstimate" WHERE "_Recno" = 8928   -- (1)
SELECT * FROM "Phrase" WHERE "_Recno" = 9718    -- (1)
SELECT * FROM "Table" WHERE "_Recno" = 4    -- (1)
SELECT * FROM "BreedEntry" WHERE "_Recno" = 3283    -- (1)

O número após a consulta é o número de vezes que a consulta exata está sendo executada, por exemplo, a consulta SELECT * FROM "Patient" WHERE "_Recno" = 35051está sendo executada 106 vezes, com esse _Recno . Na verdade, existem 1.031 consultas sendo executadas para criar este relatório (neste caso, varia) - as 23 ou mais acima são as consultas distintas .

Agora, cada consulta acima é executada muito, muito rápido, estamos falando de algumas dezenas de microssegundos em cada caso. Na verdade, se você somar todas as 1.031 consultas usadas para fazer este relatório, o tempo total usado para todas elas é de apenas 59.193 microssegundos, ou apenas 59 milissegundos.

Portanto, o problema e o atraso parecem ser a sobrecarga - embora haja apenas cerca de 59 ms de tempo real do banco de dados, o relatório leva cerca de 10 a 15 segundos para ser executado para o cliente, pois está indo e voltando com mais de 1.000 consultas.

Observe que, na maioria dos casos, o aplicativo cliente e o SQL Server estão na mesma máquina e várias instâncias do cliente são acessadas por meio do RDP. Em alguns casos, o cliente está em uma máquina diferente na LAN e imagino que o desempenho seria pior lá. Mas você pode considerar que, na maioria dos casos, não deve haver um problema de rede, pois o aplicativo cliente e o SQL Server estão na mesma caixa física.

Os dez segundos são até meio aceitáveis, o problema é que em horários mais movimentados isso pode aumentar para até um minuto ou mais.

Alguma ideia de como lidar com a otimização disso? Se fosse um aplicativo que eu tivesse acesso à fonte, obviamente substituiria tudo isso por uma ou algumas consultas que usavam junções, mas isso não é uma opção, o aplicativo é uma caixa preta - tudo o que posso fazer é otimizar do lado do SQL Server.

Falando com o cliente, embora o desempenho seja ruim, esteja ele usando o RDP ou a instalação de um aplicativo cliente remoto, o desempenho é muito pior com o aplicativo cliente remoto e isso é mais um problema para eles. Portanto, qualquer sugestão sobre coisas que eu possa observar para melhorar o desempenho lá, em relação à rede ou qualquer outra coisa, seria apreciada. Uma coisa a observar é que esta caixa do SQL 2014 agora está virtualizada, antes eles estavam usando, acho, 2008 ou 2012, mas não foi virtualizado - eles dizem que esse relatório era mais rápido naquela época. Eles têm outros motivos para querer que seja virtualizado; removê-lo da virtualização não é uma opção.

Ele se conecta usando a autenticação do Windows e (tenho certeza) TCP/IP. Acho que não conseguiria mudar isso. Não está caindo e restabelecendo as conexões, pelo menos, parece estar usando o pool de conexões.

Eu uso o Hibernate no meu trabalho diário e já me deparei com esse tipo de cenário antes, com o ORM gerando milhares de consultas, e minha solução usual é olhar para a estratégia de busca (carregamento preguiçoso vs carregamento ansioso) no código ou mesmo no caso de relatórios, geralmente reescreve tudo em SQL. Neste caso, embora o software seja o que é, um executável do Windows, e não há nada que eu possa fazer sobre isso, só posso abordar o lado SQL.

Meu entendimento é que o fornecedor não oferece mais suporte a esta versão específica e voltou para uma versão que usa arquivos simples em vez de SQL. Isso não funcionaria para o cliente - eles têm esse banco de dados integrado a várias outras coisas. É um software de nicho e, como muito desse tipo de software, é tecnicamente terrível no back-end, mas possui a funcionalidade de que o usuário do nicho precisa. De qualquer forma, não posso mudar o software, apenas o que se passa no SQL Server. Era inutilizável e eu o tornei utilizável, então fiz progressos trabalhando dentro dessas restrições.

Não há bloqueio ou bloqueio, verifiquei isso. Na verdade, esse foi o principal problema de desempenho que consertei, mas não houve nada bloqueando o tempo suficiente para ser registrado no último mês ou mais. A memória não é realmente um problema, pois o SQL Server Express é limitado a 1 GB de qualquer maneira. Ao olhar para ele, embora eu não ache que haja um problema de memória, disco, se houver, parece ser o maior ponto de estrangulamento de hardware.

sql-server sql-server-2014
  • 2 2 respostas
  • 782 Views

2 respostas

  • Voted
  1. Best Answer
    Solomon Rutzky
    2016-10-25T07:01:16+08:002016-10-25T07:01:16+08:00

    O fato de as consultas serem idênticas não é o problema e, de certa forma, ajuda, pois o Plano de Execução é armazenado em cache e as páginas de dados necessárias para a consulta ainda devem ser armazenadas em cache. O problema tenderia a ser a sobrecarga por conexão de autenticação e inicialização da sessão.

    A primeira coisa a verificar é: o "pooling de conexões" está sendo usado? Você pode testar isso usando o SQL Server Profiler, selecione o evento "RPC:Completed" na categoria "Stored Procedures", certifique-se de que "TextData", "ClientProcessID" e "SPID" estejam marcados para esse evento (no pelo menos, você pode selecionar outras colunas, se quiser). Em seguida, vá em "Filtros de coluna", selecione "TextData" e na condição "Curtir" adicione a seguinte condição: exec sp[_]reset[_]connection. Agora execute esse rastreamento. Se você vir instâncias de exec sp_reset_connection chegando, isso se deve ao uso do pool de conexões. Mas isso não significa necessariamente que é este aplicativo que o está usando. Então olhe para um dos "SPID"

    SELECT sssn.login_time,
           DATEDIFF(MILLISECOND, conn.connect_time, sssn.login_time)
                      AS [MillisecondsBetweenConnectionAndSessionStart],
           conn.*,
           sssn.[program_name],
           sssn.host_process_id,
           sssn.client_interface_name,
           sssn.login_name,
           qry.[text]
    FROM sys.dm_exec_connections conn
    INNER JOIN sys.dm_exec_sessions sssn
            ON sssn.session_id = conn.session_id
    OUTER APPLY sys.dm_exec_sql_text(conn.most_recent_sql_handle) qry
    WHERE conn.session_id <> conn.most_recent_session_id
    OR    DATEDIFF(MILLISECOND, conn.connect_time, sssn.login_time) > 50
    ORDER BY conn.connect_time;
    

    O campo na extrema direita/extremidade tem a consulta mais recente executada. Isso deve confirmar que a sessão é o aplicativo em questão (pelo que está fazendo).

    SE você não conseguir encontrar nenhuma evidência de uso do pool de conexões em geral, ou pelo menos usado para este aplicativo, a string de conexão usada pelo aplicativo precisa ser atualizada para o pool de conexões habilitado.

    ...na maioria dos casos, o aplicativo cliente e o SQL Server estão na mesma máquina e várias instâncias do cliente são acessadas por meio do RDP. Em alguns casos, o cliente está em uma máquina diferente na LAN...

    Com base nas informações acima da pergunta, parece que há vários clientes se conectando, correto? Nesse caso, o pool de conexões, embora provavelmente ainda seja uma boa ideia e útil, será menos eficaz, pois cada cliente mantém seu próprio pool de conexões. Ou seja, 5 instâncias (ou qualquer número delas) ainda criarão 5 pools separados para conexões, e cada um reduzirá a sobrecarga de inicialização da conexão para seu respectivo aplicativo, mas não pode reduzir a sobrecarga além disso/para uma única conexão compartilhada). Lembre-se também de que, mesmo com o pool de conexões, se um aplicativo não fechar corretamente suas conexões antes de tentar abrir novas, você ainda terá várias conexões/sessões provenientes de uma determinada instância do aplicativo.

    Nesse caso, e no caso de um único aplicativo que não está usando o pool de conexão e não pode ter sua String de conexão atualizada para usar o pool de conexão, então, se possível atualizar o aplicativo, seria bastante útil implementar uma camada de cache como Redis ou memcache (e acredito que AWS e Azure oferecem soluções de cache baseadas em nuvem). Esses acessos repetitivos geralmente podem ser armazenados em cache e ignorar completamente o RDBMS (por um período de tempo especificado, é claro), o que é uma grande parte do motivo pelo qual essas coisas existem.


    Agora que acabei de acompanhar os comentários recentes sobre a questão, parece que provavelmente nem a string de conexão nem qualquer parte do aplicativo podem ser modificadas. Nesse caso, não há muito o que fazer, exceto possivelmente verificar se as conexões feitas a partir do aplicativo que está sendo executado no mesmo servidor do SQL Server estão usando Memória Compartilhada ou TCP/IP para conectar e se é TCP /IP para as conexões do mesmo servidor, verifique se o protocolo de Memória Compartilhada está habilitado para SQL Server e, se não estiver, habilite-o. Esta não é uma melhoria garantida, pois é possível que a string de conexão force o protocolo a ser TCP/IP (por exemplo, usando a sintaxe de: server=tcp:{something}), mas ainda vale a pena tentar, pois essa sintaxe provavelmente não está sendo usada.



    ATUALIZAR

    do comentário sobre esta resposta:

    [a consulta acima] não me dá nada, mas olhar sys.dm_exec_connections.connect_timepara o aplicativo em questão é de várias horas a dias atrás. ... em todos os casos ... a diferença entre a conexão e o início da sessão é sempre inferior a 50ms (entre 3 e 16)

    Isso pode muito bem indicar o problema, ou pelo menos uma grande parte dele. Se uma conexão foi feita várias horas a dias atrás e a sessão começou imediatamente depois disso, o aplicativo é um aplicativo de desktop que faz uma única conexão e sessão na qual executa todas as consultas (semelhante ao funcionamento das guias de consulta do SSMS) ou o código do aplicativo incorretamente não está fechando o objeto de conexão todas as vezes, caso em que você pode estar vendo muitas conexões simultâneas, muitas das quais foram efetivamente abandonadas, mas ainda ocupando memória (e no SQL Server Express, pode haver uma conexão limite de qualquer maneira).

    Tente a seguinte consulta, adaptada da consulta original acima:

    SELECT conn.connect_time,
           sssn.login_time,
           CASE WHEN conn.last_read > conn.last_write THEN conn.last_read
                ELSE conn.last_write END AS [LastActivity],
           GETDATE() AS [Now],
           DATEDIFF(MILLISECOND, conn.connect_time, sssn.login_time)
                      AS [MillisecondsBetweenConnectionAndSessionStart],
           DATEDIFF(MILLISECOND, sssn.login_time, CASE WHEN conn.last_read > conn.last_write
                                                   THEN conn.last_read ELSE conn.last_write END)
                      AS [MillisecondsBetweenSessionStartAndLastActivity],
           DATEDIFF(MILLISECOND, CASE WHEN conn.last_read > conn.last_write
                             THEN conn.last_read ELSE conn.last_write END, GETDATE())
                      AS [MillisecondsBetweenLastActivityAndNow],
           sssn.[program_name],
           sssn.host_process_id,
           sssn.client_interface_name,
           sssn.login_name,
           qry.[text],
           conn.*
    FROM sys.dm_exec_connections conn
    INNER JOIN sys.dm_exec_sessions sssn
            ON sssn.session_id = conn.session_id
    OUTER APPLY sys.dm_exec_sql_text(conn.most_recent_sql_handle) qry
    WHERE  sssn.is_user_process = 1
    ORDER BY [MillisecondsBetweenLastActivityAndNow] DESC;
    

    Se o pool de conexões estiver sendo usado, você verá linhas com valores altos para o MillisecondsBetweenConnectionAndSessionStartcampo, mas valores muito mais baixos para o MillisecondsBetweenSessionStartAndLastActivitycampo. A razão é que uma conexão é estabelecida e reutilizada. Cada vez que a conexão é reutilizada, o login_timeredefine para o SqlConnection.Openevento mais recente e imediatamente executa a consulta.

    Se você tiver um aplicativo de desktop com uma conexão estável (como SSMS), verá o comportamento oposto ao pool de conexão: em vez disso, você terá linhas com valores baixos para o MillisecondsBetweenConnectionAndSessionStartcampo, mas valores muito mais altos para o MillisecondsBetweenSessionStartAndLastActivitycampo. A razão é que a conexão, uma vez estabelecida, nunca é fechada e simplesmente tem consultas continuadas a serem executadas nela.

    Se você tiver um aplicativo que não está fechando suas conexões, mas ainda está abrindo novas conexões (devido a um erro ou mal-entendido de como funciona o pool de conexões - estando o pool ativado ou não), você não apenas terá muito de linhas, mas terão um valor baixo para o MillisecondsBetweenConnectionAndSessionStartcampo, mas valores muito mais altos para o MillisecondsBetweenLastActivityAndNowcampo. Isso acontece se uma conexão for feita, usada uma vez e depois SqlConnection.Open()chamada antes de chamar SqlConnection.Close()ou SqlConnection.Dispose()( Dispose()chamará Close()e será chamada automaticamente se o SqlConnectionobjeto tiver sido criado em uma using()construção).

    • 4
  2. usr
    2016-10-25T06:20:15+08:002016-10-25T06:20:15+08:00

    Se você não pode alterar as consultas, não pode otimizar isso. A partir dos números que você postou, quase todo o tempo não é gasto na execução da consulta.

    Agora, cada consulta acima é executada muito, muito rápido, estamos falando de algumas dezenas de microssegundos em cada caso. Na verdade, se você somar todas as 1.031 consultas usadas para fazer este relatório, o tempo total usado para todas elas é de apenas 59.193 microssegundos, ou apenas 59 milissegundos.

    Portanto, mesmo que você otimize a execução da consulta para zero, isso não terá muito impacto.

    (Na verdade, eu não compro o número 10us. Mesmo um SELECT NULLleva ~ 150us de ponta a ponta quando executado usando ADO.NET no transporte de memória compartilhada em uma conexão aberta. Mas qualitativamente pode ser verdade.)

    • 2

relate perguntas

  • SQL Server - Como as páginas de dados são armazenadas ao usar um índice clusterizado

  • Preciso de índices separados para cada tipo de consulta ou um índice de várias colunas funcionará?

  • Quando devo usar uma restrição exclusiva em vez de um índice exclusivo?

  • Quais são as principais causas de deadlocks e podem ser evitadas?

  • Como determinar se um Índice é necessário ou necessário

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve