Truques de ajuste de desempenho favoritos [fechado]

126

Quando você tem uma consulta ou procedimento armazenado que precisa de ajuste de desempenho, quais são as primeiras coisas que você tenta?

Terrapin
fonte
Aqui estão alguns truques do SQL Server Query Optimization
SQLMenace 22/08/08
Concordo que isso não é construtivo e pode ser pesquisado no Google, mas por que ele tem 118 uv ?! :)
FLICKER

Respostas:

114

Aqui está a lista prática de coisas que sempre dou a alguém que me pergunta sobre otimização.
Usamos principalmente o Sybase, mas a maioria dos conselhos se aplica a todos.

O SQL Server, por exemplo, vem com uma série de bits de monitoramento / ajuste de desempenho, mas se você não tiver nada parecido (e talvez até o faça), considerarei o seguinte ...

99% dos problemas que eu vi são causados ​​por colocar muitas tabelas em uma junção . A correção para isso é fazer metade da junção (com algumas das tabelas) e armazenar em cache os resultados em uma tabela temporária. Em seguida, faça o restante da consulta ingressando nessa tabela temporária.

Lista de verificação de otimização de consulta

  • Execute UPDATE STATISTICS nas tabelas subjacentes
    • Muitos sistemas executam isso como um trabalho semanal agendado
  • Excluir registros de tabelas subjacentes (possivelmente arquivar os registros excluídos)
    • Considere fazer isso automaticamente uma vez por dia ou uma vez por semana.
  • Recriar índices
  • Reconstruir tabelas (saída / entrada de dados bcp)
  • Despejar / recarregar o banco de dados (drástico, mas pode corrigir corrupção)
  • Crie um índice novo e mais apropriado
  • Execute o DBCC para verificar se há uma possível corrupção no banco de dados
  • Fechaduras / deadlocks
    • Garantir que nenhum outro processo seja executado no banco de dados
      • Especialmente DBCC
    • Você está usando bloqueio de linha ou página?
    • Bloqueie as tabelas exclusivamente antes de iniciar a consulta
    • Verifique se todos os processos estão acessando tabelas na mesma ordem
  • Os índices estão sendo usados ​​adequadamente?
    • As uniões usarão o índice apenas se ambas as expressões forem exatamente do mesmo tipo de dados
    • O índice será usado apenas se os primeiros campos no índice corresponderem na consulta
    • Os índices clusterizados são usados ​​quando apropriado?
      • dados do intervalo
      • WHERE campo entre valor1 e valor2
  • Junções pequenas são Junções agradáveis
    • Por padrão, o otimizador considerará apenas as tabelas 4 de cada vez.
    • Isso significa que nas junções com mais de 4 tabelas, há uma boa chance de escolher um plano de consulta não ideal
  • Quebrar a junção
    • Você pode terminar a junção?
    • Pré-selecionar chaves estrangeiras em uma tabela temporária
    • Faça metade da junção e coloque os resultados em uma tabela temporária
  • Você está usando o tipo certo de tabela temporária?
    • #tempAs tabelas podem ter um desempenho muito melhor que as @tablevariáveis ​​com grandes volumes (milhares de linhas).
  • Atualizar tabelas de resumo
    • Construir com gatilhos nas tabelas subjacentes
    • Crie diariamente / a cada hora / etc.
    • Crie ad-hoc
    • Crie de forma incremental ou desmontagem / reconstrução
  • Veja qual é o plano de consulta com SET SHOWPLAN ON
  • Veja o que realmente está acontecendo com SET STATS IO ON
  • Forçar um índice usando o pragma: (index: myindex)
  • Forçar a ordem da tabela usando SET FORCEPLAN ON
  • Sniffing do parâmetro:
    • Divida o procedimento armazenado em 2
    • chame proc2 de proc1
    • permite que o otimizador escolha o índice no proc2 se @parameter tiver sido alterado pelo proc1
  • Você pode melhorar seu hardware?
  • A que horas você está correndo? Existe um tempo mais calmo?
  • O Replication Server (ou outro processo ininterrupto) está em execução? Você pode suspendê-lo? Execute-o, por exemplo. a cada hora?
AJ.
fonte
2
a qual bit você está se referindo?
AJ.
2
Isso é legal, mas eu gostaria que você tivesse algumas referências para algumas reivindicações. Por exemplo: eu nunca tinha ouvido falar que a otimização considera apenas 4 tabelas por vez em uma associação. Eu não entendo como isso pode estar certo. Você poderia fornecer algumas referências para isso em particular? Eu adoraria ver onde você está conseguindo isso.
SheldonH
19
  1. Tenha uma boa idéia do caminho ideal para executar a consulta em sua cabeça.
  2. Verifique o plano de consulta - sempre.
  3. Ative STATS, para poder examinar o desempenho de E / S e CPU. Concentre-se em reduzir esses números, não necessariamente o tempo de consulta (pois isso pode ser influenciado por outras atividades, cache, etc.).
  4. Procure um grande número de linhas entrando em um operador, mas pequenos números saindo. Normalmente, um índice ajudaria limitando o número de linhas entrando (o que salva leituras de disco).
  5. Primeiro, foque na subárvore de maior custo. A alteração dessa subárvore geralmente pode alterar o plano de consulta inteiro.
  6. Problemas comuns que já vi são:
    • Se houver muitas associações, às vezes o Sql Server optará por expandir as associações e aplicar as cláusulas WHERE. Geralmente, você pode corrigir isso movendo as condições WHERE para a cláusula JOIN ou uma tabela derivada com as condições incorporadas. As visualizações podem causar os mesmos problemas.
    • Junções subótimas (LOOP x HASH x MERGE). Minha regra geral é usar uma junção LOOP quando a linha superior tiver muito poucas linhas comparadas à parte inferior, uma MERGE quando os conjuntos forem aproximadamente iguais e ordenados e um HASH para todo o resto. Adicionar uma dica de junção permitirá testar sua teoria.
    • Parâmetro cheirando. Se você executou o processo armazenado com valores irreais no início (por exemplo, para teste), o plano de consulta em cache pode ficar abaixo do ideal para seus valores de produção. A execução novamente com RECOMPILE deve verificar isso. Para alguns procs armazenados, especialmente aqueles que lidam com intervalos de tamanhos variados (digamos, todas as datas entre hoje e ontem - o que implicaria uma INDEX SEEK - ou, todas as datas entre o ano passado e este ano -, seria melhor usar um INDEX SCAN ), pode ser necessário executá-lo com RECOMPILE sempre.
    • Recuo incorreto ... Ok, o Sql Server não tem problema com isso - mas acho impossível entender uma consulta até que eu tenha corrigido a formatação.
Mark Brackett
fonte
1
+1 para a inclusão de recuo incorreto. Formatar é a chave! :)
mwigdahl
18

Um pouco fora do tópico, mas se você tiver controle sobre esses problemas ...
Alto nível e Alto impacto.

  • Para ambientes de E / S alta, verifique se os discos são para RAID 10 ou RAID 0 + 1 ou alguma implementação aninhada do RAID 1 e RAID 0.
  • Não use unidades com menos de 1500K.
  • Verifique se seus discos são usados ​​apenas para o seu banco de dados. IE sem registro no sistema operacional.
  • Desative o crescimento automático ou recurso semelhante. Deixe o banco de dados usar todo o armazenamento previsto. Não necessariamente o que está sendo usado no momento.
  • projete seu esquema e índices para as consultas de tipo.
  • se for uma tabela de tipo de log (somente inserção) e deve estar no banco de dados, não a indexe.
  • se você estiver distribuindo relatórios (o complexo seleciona com muitas junções), você deve criar um armazém de dados com um esquema em estrela ou floco de neve.
  • Não tenha medo de replicar dados em troca de desempenho!
jason saldo
fonte
8

CREATE INDEX

Verifique se há índices disponíveis para suas cláusulas WHEREe JOIN. Isso agilizará bastante o acesso aos dados.

Se o seu ambiente é um data mart ou armazém, os índices deverão ser abundantes para quase todas as consultas possíveis.

Em um ambiente transacional , o número de índices deve ser menor e suas definições mais estratégicas, para que a manutenção do índice não arraste recursos. (A manutenção do índice é quando as folhas de um índice devem ser alteradas para refletir uma alteração na tabela subjacente, como acontece comINSERT, UPDATE, e DELETEoperações.)

Além disso, lembre-se da ordem dos campos no índice - quanto mais seletivo (cardinalidade maior) um campo, mais cedo no índice ele deve aparecer. Por exemplo, digamos que você esteja consultando automóveis usados:

SELECT   i.make, i.model, i.price
FROM     dbo.inventory i
WHERE    i.color = 'red'
  AND    i.price BETWEEN 15000 AND 18000

Preço geralmente tem maior cardinalidade. Pode haver apenas algumas dezenas de cores disponíveis, mas possivelmente milhares de preços diferentes.

Dessas opções de índice, idx01fornece o caminho mais rápido para satisfazer a consulta:

CREATE INDEX idx01 ON dbo.inventory (price, color)
CREATE INDEX idx02 ON dbo.inventory (color, price)

Isso ocorre porque menos carros satisfazem o preço do que a escolha da cor, dando ao mecanismo de consulta muito menos dados para analisar.

Eu sou conhecido por ter dois índices muito semelhantes, diferindo apenas na ordem dos campos para acelerar as consultas (nome, sobrenome) em um e (sobrenome, nome) no outro.

Will SQL para Alimentos
fonte
6

Um truque que aprendi recentemente é que o SQL Server pode atualizar variáveis ​​locais e campos em uma instrução de atualização.

UPDATE table
SET @variable = column = @variable + otherColumn

Ou a versão mais legível:

UPDATE table
SET
    @variable = @variable + otherColumn,
    column = @variable

Usei isso para substituir cursores / junções complicados ao implementar cálculos recursivos e também obtive muito desempenho.

A seguir, detalhes e código de exemplo que fizeram melhorias fantásticas no desempenho: http://geekswithblogs.net/Rhames/archive/2008/10/28/calculating-running-totals-in-sql-server-2005---the-optimal. aspx

jandersson
fonte
5

Assumindo o MySQL aqui, use EXPLAIN para descobrir o que está acontecendo com a consulta, verifique se os índices estão sendo usados ​​da maneira mais eficiente possível e tente eliminar as classificações de arquivos. MySQL de alto desempenho: otimização, backups, replicação e muito mais é um ótimo livro sobre esse tópico, assim como o Blog de desempenho do MySQL .

davidmytton
fonte
3
Isso é bom para o MySQL, mas a questão foi marcada com "sqlserver". Ainda assim, é uma boa coisa fazer isso. A coisa análoga a ser feita no SSMS é usar "Exibir plano de execução estimado" e "Incluir plano de execução real". Se você pode eliminar varreduras enormes de tabela e usar pesquisas de índice em cluster, estará no caminho certo para obter o desempenho ideal.
eksortso
5

@Terrapin, existem algumas outras diferenças entre isnull e coalescer que merecem ser mencionadas (além da conformidade com a ANSI, que é importante para mim).

Coalesce vs. IsNull

AlexCuse
fonte
3

Às vezes, no SQL Server, se você usar um OR em uma cláusula where, ele realmente terá um desempenho. Em vez de usar o OR, basta fazer duas seleções e uni-las. Você obtém os mesmos resultados em 1000x a velocidade.

Ryan
fonte
Eu já vi esse comportamento inexplicável.
Esen
2

Veja a cláusula where - verifique o uso de índices / verifique se nada está sendo feito de bobo

where SomeComplicatedFunctionOf(table.Column) = @param --silly
Mike
fonte
2

Geralmente, começarei com as junções - eliminarei cada uma delas da consulta, uma de cada vez, e executarei novamente a consulta para ter uma idéia se houver uma junção específica com a qual estou tendo problemas.

John Christensen
fonte
2

Em todas as minhas tabelas temporárias, gosto de adicionar restrições exclusivas (quando apropriado) para criar índices e chaves primárias (quase sempre).

declare @temp table(
    RowID int not null identity(1,1) primary key,
    SomeUniqueColumn varchar(25) not null,
    SomeNotUniqueColumn varchar(50) null,
    unique(SomeUniqueColumn)
)
Seibar
fonte
2

Eu criei o hábito de sempre usar variáveis ​​de ligação. É possível que as variáveis ​​de ligação não ajudem se o RDBMS não armazenar em cache instruções SQL. Mas se você não usar variáveis ​​de ligação, o RDBMS não terá chance de reutilizar planos de execução de consultas e instruções SQL analisadas. A economia pode ser enorme: http://www.akadia.com/services/ora_bind_variables.html . Eu trabalho principalmente com Oracle, mas o Microsoft SQL Server funciona da mesma maneira.

Na minha experiência, se você não sabe se está ou não usando variáveis ​​de ligação, provavelmente não está. Se o idioma do seu aplicativo não os suportar, encontre um que suporte. Às vezes, você pode corrigir a consulta A usando variáveis ​​de ligação para a consulta B.

Depois disso, converso com nosso DBA para descobrir o que está causando mais dor no RDBMS. Observe que você não deve perguntar "Por que essa consulta é lenta?" É como pedir ao seu médico para tirar seu apêndice. Certifique-se de que sua consulta possa ser o problema, mas é provável que algo esteja errado. Como desenvolvedores, tendemos a pensar em termos de linhas de código. Se uma linha estiver lenta, corrija-a. Mas um RDBMS é um sistema realmente complicado e sua consulta lenta pode ser o sintoma de um problema muito maior.

Muitas dicas de ajuste de SQL são ídolos de cultos de carga. Na maioria das vezes, o problema não está relacionado ou está minimamente relacionado à sintaxe usada, portanto, normalmente é melhor usar a sintaxe mais limpa possível. Em seguida, você pode começar a procurar maneiras de ajustar o banco de dados (não a consulta). Apenas ajuste a sintaxe quando isso falhar.

Como qualquer ajuste de desempenho, sempre colete estatísticas significativas. Não use o tempo do relógio de parede, a menos que seja a experiência do usuário que você está ajustando. Em vez disso, observe coisas como tempo de CPU, linhas buscadas e blocos lidos no disco. Muitas vezes as pessoas otimizam para a coisa errada.

Jon Ericson
fonte
2

Primeira etapa: veja o plano de execução de consultas!
TableScan ->
aviso ruim de NestedLoop -> meh
TableScan atrás de um NestedLoop -> DOOM!

DEFINIR ESTATÍSTICAS IO ON
DEFINIR ESTATÍSTICAS HORA

Amy B
fonte
2

Executar a consulta usando WITH (NoLock) é praticamente uma operação padrão em meu lugar. Qualquer um que for pego executando consultas nas tabelas de dezenas de gigabytes sem que seja retirado e filmado.

Valerion
fonte
2
Isso deve ser usado criteriosamente, não habitualmente. Trancar não é mau, apenas incompreendido.
2

Converta as consultas NOT IN para JOYS OUTER ESQUERDA, se possível. Por exemplo, se você deseja encontrar todas as linhas na Tabela1 que não são usadas por uma chave estrangeira na Tabela2, você pode fazer isso:

SELECT *
FROM Table1
WHERE Table1.ID NOT IN (
    SELECT Table1ID
    FROM Table2)

Mas você obtém um desempenho muito melhor com isso:

SELECT Table1.*
FROM Table1
LEFT OUTER JOIN Table2 ON Table1.ID = Table2.Table1ID
WHERE Table2.ID is null
Martin Brown
fonte
1

@ DavidM

Supondo que o MySQL aqui, use EXPLAIN para descobrir o que está acontecendo com a consulta, verifique se os índices estão sendo usados ​​da maneira mais eficiente possível ...

No SQL Server, o plano de execução oferece a mesma coisa: informa quais índices estão sendo atingidos, etc.

Seibar
fonte
1

Indexe a (s) tabela (s) pelo (s) clm (s) filtrado (s)

csmba
fonte
1

Não necessariamente um truque de desempenho do SQL em si, mas definitivamente relacionado:

Uma boa idéia seria usar o memcached sempre que possível, pois seria muito mais rápido buscar os dados pré-compilados diretamente da memória do que obtê-los do banco de dados. Há também uma versão do MySQL que foi incorporada ao memcached (de terceiros).

Andy
fonte
1

Verifique se o comprimento do seu índice é o menor possível. Isso permite que o banco de dados leia mais chaves de cada vez no sistema de arquivos, acelerando suas junções. Suponho que isso funcione com todos os bancos de dados, mas sei que é uma recomendação específica para o MySQL.

Barrett Conrad
fonte
1

Eu procuro:

  • Desenrole qualquer loop CURSOR e converta em instruções UPDATE / INSERT baseadas em conjunto.
  • Procure qualquer código de aplicativo que:
    • Chama um SP que retorna um grande conjunto de registros,
    • Em seguida, no aplicativo, percorre cada registro e chama um SP com parâmetros para atualizar registros.
    • Converta isso em um SP que faz todo o trabalho em uma transação.
  • Qualquer SP que faça muita manipulação de string. É uma evidência de que os dados não estão estruturados corretamente / normalizados.
  • Qualquer SP que reinvente a roda.
  • Qualquer SP que eu não consigo entender o que está tentando fazer em um minuto!
Cara
fonte
1
SET NOCOUNT ON

Normalmente, a primeira linha dentro dos meus procedimentos armazenados, a menos que eu realmente precise usar @@ROWCOUNT.

travis
fonte
2
@@ ROWCOUNT está definido de qualquer maneira. NOCOUNT desativa as instruções "xx linhas afetadas".
Sklivvz
Isso realmente faz alguma diferença significativa no desempenho?
JohnFx
Sim, a contagem não é calculada automaticamente sempre que uma instrução SQL é executada. É fácil o suficiente para comparar uma consulta com e sem, para ver se isso faz diferença.
travis
A contagem é rastreada no SQL Server de qualquer maneira. Qualquer diferença de desempenho que você vê é porque as contagens precisam passar pela rede até o seu front end. Se você estiver fazendo um único SELECT, não fará uma diferença significativa. Se você tem um loop com 100000 inserções, é muito mais pela rede.
Tom H
1

No SQL Server, use a diretiva nolock. Ele permite que o comando select seja concluído sem ter que esperar - geralmente outras transações serão concluídas.

SELECT * FROM Orders (nolock) where UserName = 'momma'
jinsungy
fonte
3
NOLOCK é apenas para consultas para as quais você não se preocupam com resultados corretos
Mark Sowul
1

Remova os cursores sempre que não forem necessários.

Terrapin
fonte
Sim, os cursores são uma maldição! ;)
Sklivvz 01/10/08
8
Ugh. Não jogue isso fora não qualificado assim. Cursores são como armas. Eles não são ruins por si mesmos, é só que as pessoas fazem coisas muito ruins com eles.
JohnFx
1

Remova as chamadas de função nos Sprocs, onde muitas linhas chamarão a função.

Meu colega usou chamadas de função (obtendo lastlogindate do userid como exemplo) para retornar conjuntos de registros muito amplos.

Com a otimização, substituí as chamadas de função no sproc pelo código da função: reduzi o tempo de execução de muitos sprocs de> 20 segundos para <1.

calisto
fonte
0
  • Prefixe todas as tabelas com dbo. para evitar recompilações.
  • Visualize planos de consulta e procure por verificações de tabela / índice.
  • Em 2005, vasculhe as visualizações de gerenciamento em busca de índices ausentes.
Stu
fonte
0

Não prefixe os nomes dos Procedimentos armazenados com "sp_" porque todos os procedimentos do sistema começam com "sp_", e o SQL Server terá que procurar mais para encontrar seu procedimento quando for chamado.

Terrapin
fonte
1
Você realmente comparou este? Se o SQL Server estiver fazendo o que é razoável (usando um algoritmo de hash para localizar o Proc armazenado), isso não fará diferença. De fato, se o SQL Server não estivesse fazendo isso, parece que o desempenho do sistema fedia (já que provavelmente chama de seus próprios procs).
John Stauffer
1
Eu acho que isso cai no balde da otimização prematura. É provavelmente uma boa prática para evitar confusão para as pessoas, mas como uma dica de otimização ... D-
JohnFx
0

Leituras sujas -

set transaction isolation level read uncommitted

Impede bloqueios mortos onde a integridade transacional não é absolutamente necessária (o que geralmente é verdadeiro)

Terrapin
fonte
1
Sim, mas isso pode levar a erros estranhos que são MUITO difíceis de encontrar.
Grant Johnson
0

Eu sempre vou primeiro ao SQL Profiler (se é um procedimento armazenado com muitos níveis de aninhamento) ou ao planejador de execução de consulta (se são algumas instruções SQL sem aninhamento). 90% do tempo, você pode encontrar o problema imediatamente com uma dessas duas ferramentas.

mwigdahl
fonte