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 / 52552
Accepted
SpeedBirdNine
SpeedBirdNine
Asked: 2013-10-30 22:00:21 +0800 CST2013-10-30 22:00:21 +0800 CST 2013-10-30 22:00:21 +0800 CST

O índice não torna a execução mais rápida e, em alguns casos, está retardando a consulta. Por que é tão?

  • 772

Eu estava experimentando índices para acelerar as coisas, mas no caso de uma junção, o índice não está melhorando o tempo de execução da consulta e, em alguns casos, está diminuindo a velocidade.

A consulta para criar a tabela de teste e preenchê-la com dados é:

CREATE TABLE [dbo].[IndexTestTable](
    [id] [int] IDENTITY(1,1) PRIMARY KEY,
    [Name] [nvarchar](20) NULL,
    [val1] [bigint] NULL,
    [val2] [bigint] NULL)

DECLARE @counter INT;
SET @counter = 1;

WHILE @counter < 500000
BEGIN
    INSERT INTO IndexTestTable
      (
        -- id -- this column value is auto-generated
        NAME,
        val1,
        val2
      )
    VALUES
      (
        'Name' + CAST((@counter % 100) AS NVARCHAR),
        RAND() * 10000,
        RAND() * 20000
      );

    SET @counter = @counter + 1;
END

-- Index in question
CREATE NONCLUSTERED INDEX [IndexA] ON [dbo].[IndexTestTable]
(
    [Name] ASC
)
INCLUDE (   [id],
    [val1],
    [val2])

Agora a consulta 1, que foi melhorada (apenas um pouco, mas a melhoria é consistente) é:

SELECT *
FROM   IndexTestTable I1
       JOIN IndexTestTable I2
            ON  I1.ID = I2.ID
WHERE  I1.Name = 'Name1'

Estatísticas e plano de execução sem índice (neste caso, a tabela usa o índice clusterizado padrão):

(5000 row(s) affected)
Table 'IndexTestTable'. Scan count 2, logical reads 5580, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 109 ms,  elapsed time = 294 ms.

insira a descrição da imagem aqui

Agora com o índice ativado:

(5000 row(s) affected)
Table 'IndexTestTable'. Scan count 2, logical reads 2819, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 94 ms,  elapsed time = 231 ms.

insira a descrição da imagem aqui

Agora, a consulta que fica mais lenta devido ao índice (a consulta não tem sentido, pois é criada apenas para teste):

SELECT I1.Name,
       SUM(I1.val1),
       SUM(I1.val2),
       MIN(I2.Name),
       SUM(I2.val1),
       SUM(I2.val2)
FROM   IndexTestTable I1
       JOIN IndexTestTable I2
            ON  I1.Name = I2.Name
WHERE   
       I2.Name = 'Name1'
GROUP BY
       I1.Name

Com o índice clusterizado ativado:

(1 row(s) affected)
Table 'IndexTestTable'. Scan count 4, logical reads 60, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 1, logical reads 155106, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 17207 ms,  elapsed time = 17337 ms.

insira a descrição da imagem aqui

Agora com o Index desabilitado:

(1 row(s) affected)
Table 'IndexTestTable'. Scan count 5, logical reads 8642, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 2, logical reads 165212, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 17691 ms,  elapsed time = 9073 ms.

insira a descrição da imagem aqui

As perguntas são:

  1. Mesmo que o índice seja sugerido pelo SQL Server, por que ele retarda as coisas por uma diferença significativa?
  2. Qual é a junção Nested Loop que está demorando mais e como melhorar seu tempo de execução?
  3. Há algo que estou fazendo de errado ou deixei passar?
  4. Com o índice padrão (somente na chave primária), por que leva menos tempo e com o índice não clusterizado presente, para cada linha na tabela de junção, a linha da tabela unida deve ser encontrada mais rapidamente, porque a junção está na coluna Nome na qual o índice foi criado. Isso se reflete no plano de execução da consulta e o custo de Index Seek é menor quando o IndexA está ativo, mas por que ainda mais lento? Além disso, o que está na junção externa esquerda do loop aninhado que está causando a lentidão?

Usando o SQLServer 2012

sql-server index
  • 2 2 respostas
  • 25235 Views

2 respostas

  • Voted
  1. Best Answer
    Paul White
    2013-10-31T21:49:41+08:002013-10-31T21:49:41+08:00

    Mesmo que o índice seja sugerido pelo SQL Server, por que ele retarda as coisas por uma diferença significativa?

    As sugestões de índice são feitas pelo otimizador de consulta. Se encontrar uma seleção lógica de uma tabela que não é bem atendida por um índice existente, pode adicionar uma sugestão de "índice ausente" à sua saída. Essas sugestões são oportunistas; eles não se baseiam em uma análise completa da consulta e não levam em consideração considerações mais amplas. Na melhor das hipóteses, eles são uma indicação de que uma indexação mais útil pode ser possível, e um DBA qualificado deve dar uma olhada.

    A outra coisa a dizer sobre sugestões de índices ausentes é que elas são baseadas no modelo de custos do otimizador, e o otimizador estima quanto o índice sugerido pode reduzir o custo estimado da consulta. As palavras-chave aqui são "modelo" e "estimativas". O otimizador de consulta sabe pouco sobre sua configuração de hardware ou outras opções de configuração do sistema - seu modelo é amplamente baseado em números fixos que produzem resultados de plano razoáveis ​​para a maioria das pessoas na maioria dos sistemas na maior parte do tempo. Além dos problemas com os números de custo exatos usados, os resultados são sempre estimativas - e as estimativas podem estar erradas.

    Qual é a junção Nested Loop que está demorando mais e como melhorar seu tempo de execução?

    Há pouco a ser feito para melhorar o desempenho da própria operação de junção cruzada; loops aninhados são a única implementação física possível para uma junção cruzada. O spool da mesa no lado interno da junção é uma otimização para evitar a nova varredura do lado interno para cada linha externa. Se esta é uma otimização de desempenho útil depende de vários fatores, mas em meus testes a consulta fica melhor sem ela. Novamente, isso é uma consequência do uso de um modelo de custo - meu sistema de CPU e memória provavelmente tem características de desempenho diferentes das suas. Não há nenhuma dica de consulta específica para evitar o spool da tabela, mas há um sinalizador de rastreamento não documentado (8690) que você pode usar para testar o desempenho da execução com e sem o spool. Se este fosse um problema real do sistema de produção, o plano sem o spool pode ser forçado usando um guia de plano baseado no plano produzido com o TF 8690 ativado. O uso de sinalizadores de rastreamento não documentados na produção não é recomendado porque a instalação se torna tecnicamente sem suporte e os sinalizadores de rastreamento podem ter efeitos colaterais indesejáveis.

    Há algo que estou fazendo de errado ou deixei passar?

    A principal coisa que você está perdendo é que, embora o plano usando o índice não clusterizado tenha um custo estimado menor de acordo com o modelo do otimizador, ele tem um problema significativo de tempo de execução. Se você observar a distribuição de linhas nos encadeamentos no plano usando o Clustered Index, provavelmente verá uma distribuição razoavelmente boa:

    plano de digitalização

    No plano que usa o Nonclustered Index Seek, o trabalho acaba sendo feito inteiramente por uma thread:

    Buscar plano

    This is a consequence of the way work is distributed among threads by parallel scan/seek operations. It is not always the case that a parallel scan will distribute work better than an index seek - but it does in this case. More complex plans might include repartitioning exchanges to redistribute work across threads. This plan has no such exchanges, so once rows are assigned to a thread, all related work is performed on that same thread. If you look at the work distribution for the other operators in the execution plan, you will see that all work is performed by the same thread as shown for the index seek.

    There are no query hints to affect row distribution among threads, the important thing is to be aware of the possibility and to be able to read enough detail in the execution plan to determine when it is causing a problem.

    With the default index (on primary key only) why does it take less time, and with the non clustered index present, for each row in the joining table, the joined table row should be found quicker, because join is on Name column on which the index has been created. This is reflected in the query execution plan and Index Seek cost is less when IndexA is active, but why still slower? Also what is in the Nested Loop left outer join that is causing the slowdown?

    It should now be clear that the nonclustered index plan is potentially more efficient, as you would expect; it is just poor distribution of work across threads at execution time that accounts for the performance issue.

    Para completar o exemplo e ilustrar algumas das coisas que mencionei, uma maneira de obter uma melhor distribuição de trabalho é usar uma tabela temporária para conduzir a execução paralela:

    SELECT
        val1,
        val2
    INTO #Temp
    FROM dbo.IndexTestTable AS ITT
    WHERE Name = N'Name1';
    
    SELECT 
        N'Name1',
        SUM(T.val1),
        SUM(T.val2),
        MIN(I2.Name),
        SUM(I2.val1),
        SUM(I2.val2)
    FROM   #Temp AS T
    CROSS JOIN IndexTestTable I2
    WHERE
        I2.Name = 'Name1'
    OPTION (FORCE ORDER, QUERYTRACEON 8690);
    
    DROP TABLE #Temp;
    

    Isso resulta em um plano que usa as buscas de índice mais eficientes, não apresenta um spool de tabela e distribui bem o trabalho entre os encadeamentos:

    plano ideal

    No meu sistema, esse plano é executado significativamente mais rápido do que a versão Clustered Index Scan.

    Se você estiver interessado em aprender mais sobre os aspectos internos da execução de consultas paralelas, talvez queira assistir à gravação da minha sessão do PASS Summit 2013 .

    • 29
  2. Adrian Sullivan
    2013-10-31T01:53:02+08:002013-10-31T01:53:02+08:00

    Não é realmente uma questão de índice, é mais uma consulta mal escrita. Você tem apenas 100 valores exclusivos de nome, o que deixa uma contagem exclusiva de 5.000 por nome.

    Então, para cada linha na tabela 1, você está juntando 5000 da tabela 2. Você pode dizer 25020004 linhas.

    Tente isso, observe que é com apenas 1 índice, o que você listou.

        DECLARE @Distincts INT
        SET @Distincts = (SELECT  TOP 1 COUNT(*) FROM IndexTestTable I1 WHERE I1.Name = 'Name1' GROUP BY I1.Name)
        SELECT I1.Name
        , @Distincts
        , SUM(I1.val1) * @Distincts
        , SUM(I1.val2) * @Distincts
        , MIN(I2.Name)
        , SUM(I2.val1)
        , SUM(I2.val2)
        FROM   IndexTestTable I1
        LEFT OUTER JOIN
    
        (
            SELECT I2.Name
            , SUM(I2.val1) val1
            , SUM(I2.val2) val2
            FROM IndexTestTable I2
            GROUP BY I2.Name
        ) I2 ON  I1.Name = I2.Name
        WHERE I1.Name = 'Name1'
        GROUP BY  I1.Name
    

    E tempo:

        SQL Server parse and compile time: 
           CPU time = 0 ms, elapsed time = 8 ms.
        Table 'IndexTestTable'. Scan count 1, logical reads 31, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    
         SQL Server Execution Times:
           CPU time = 0 ms,  elapsed time = 1 ms.
    
        (1 row(s) affected)
        Table 'IndexTestTable'. Scan count 2, logical reads 62, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
        Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    
         SQL Server Execution Times:
           CPU time = 16 ms,  elapsed time = 10 ms.
    

    insira a descrição da imagem aqui

    Você não pode culpar os índices SQL por consultas mal formadas

    • 0

relate perguntas

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

  • Quanto "Padding" coloco em meus índices?

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

  • O que significa "índice" em RDBMSs? [fechado]

  • Como criar um índice condicional no MySQL?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • 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

    Conceder acesso a todas as tabelas para um usuário

    • 5 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
    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
    pedrosanta Listar os privilégios do banco de dados usando o psql 2011-08-04 11:01:21 +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