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 / 158774
Accepted
user369117
user369117
Asked: 2016-12-22 00:30:04 +0800 CST2016-12-22 00:30:04 +0800 CST 2016-12-22 00:30:04 +0800 CST

Por que essa busca de índice causa muito mais leituras do que a varredura de índice

  • 772

Como li que as buscas de índice na maioria das vezes são preferidas às varreduras de índice, estou tentando algumas coisas.

Eu tenho uma consulta que faz 993 leituras (verificadas com o SQL Profiler) quando está usando uma varredura de índice. Ao usar uma busca de índice, ela precisa de 44.347 leituras. Parece que algo está errado, ou eu não entendo.

Esta é a consulta para varredura de índice:

select      t5.Id as t5Id
from        table1 t1
left join   table2 t2 on t2.Table1Id = t1.Id
left join   table3 t3 on t3.Table2Id = t2.Id
left join   table4 t4 on t4.Table3Id = t3.Id
left join   table5 t5 on t5.Table4Id = t4.Id

esta é a consulta para busca de índice:

select      t5.Id as t5Id
from        table1 t1
left join   table2 t2 on t2.Table1Id = t1.Id
left join   table3 t3 on t3.Table2Id = t2.Id
left join   table4 t4 on t4.Table3Id = t3.Id
left join   table5 t5 WITH (FORCESEEK) on t5.Table4Id = t4.Id

As tabelas são simples e diretas. No final, preencho-os com alguns dados fictícios, para que possam ser reproduzidos facilmente.

CREATE TABLE [dbo].[table1](
    [Id] [bigint] IDENTITY(1,1) NOT NULL,
    [Name] [nvarchar](max) NOT NULL,
CONSTRAINT [PK_table1] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO




CREATE TABLE [dbo].[table2](
    [Id] [bigint] IDENTITY(1,1) NOT NULL,
    [Table1Id] [bigint] NOT NULL,
CONSTRAINT [PK_table2] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

ALTER TABLE     [dbo].[table2] WITH CHECK
ADD CONSTRAINT  [FK_table2_table1Id] FOREIGN KEY([table1Id])
REFERENCES      [dbo].[table1] ([Id])
GO

ALTER TABLE         [dbo].[table2]
CHECK CONSTRAINT    [FK_table2_table1Id]
GO


CREATE NONCLUSTERED INDEX [IdxTable2_FKTable1Id] ON [dbo].[table2]
(
    [Table1Id] ASC
)
INCLUDE (   [Id]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO






CREATE TABLE [dbo].[table3](
    [Id] [bigint] IDENTITY(1,1) NOT NULL,
    [Table2Id] [bigint] NOT NULL,
CONSTRAINT [PK_table3] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

ALTER TABLE     [dbo].[table3] WITH CHECK
ADD CONSTRAINT  [FK_table3_table2Id] FOREIGN KEY([table2Id])
REFERENCES      [dbo].[table2] ([Id])
GO

ALTER TABLE         [dbo].[table3]
CHECK CONSTRAINT    [FK_table3_table2Id]
GO


CREATE NONCLUSTERED INDEX [IdxTable3_FKTable2Id] ON [dbo].[table3]
(
    [Table2Id] ASC
)
INCLUDE (   [Id]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO







CREATE TABLE [dbo].[table4](
    [Id] [bigint] IDENTITY(1,1) NOT NULL,
    [Table3Id] [bigint] NOT NULL,
CONSTRAINT [PK_table4] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

ALTER TABLE     [dbo].[table4] WITH CHECK
ADD CONSTRAINT  [FK_table4_table3Id] FOREIGN KEY([table3Id])
REFERENCES      [dbo].[table4] ([Id])
GO

ALTER TABLE         [dbo].[table4]
CHECK CONSTRAINT    [FK_table4_table3Id]
GO

CREATE NONCLUSTERED INDEX [IdxTable4_FKTable3Id] ON [dbo].[table4]
(
    [Table3Id] ASC
)
INCLUDE (   [Id]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO





CREATE TABLE [dbo].[table5](
    [Id] [bigint] IDENTITY(1,1) NOT NULL,
    [Table4Id] [bigint] NOT NULL,
    [Description] [nvarchar](2000) NOT NULL,
CONSTRAINT [PK_table5] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

ALTER TABLE     [dbo].[table5] WITH CHECK
ADD CONSTRAINT  [FK_table5_table4Id] FOREIGN KEY([table4Id])
REFERENCES      [dbo].[table5] ([Id])
GO

ALTER TABLE         [dbo].[table5]
CHECK CONSTRAINT    [FK_table5_table4Id]
GO

CREATE NONCLUSTERED INDEX [IdxTable5_FKTable4Id] ON [dbo].[table5]
(
    [Table4Id] ASC
)
INCLUDE (   [Id], [Description]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO


set nocount on

DECLARE @i INT = 0;
DECLARE @j INT = 0;
DECLARE @k INT = 0;
DECLARE @l INT = 10;
DECLARE @m INT = 0;

declare @table1Id bigint
declare @table2Id bigint
declare @table3Id bigint
declare @table4Id bigint


begin tran
WHILE @i < 10
BEGIN
    INSERT INTO [dbo].[table1] ([Name]) VALUES (cast(@i as nvarchar(10)))
    SELECT @table1Id = SCOPE_IDENTITY()

    WHILE @j < 10
    BEGIN
        INSERT INTO [dbo].[table2] ([Table1Id]) VALUES (@table1Id)
        SELECT @table2Id = SCOPE_IDENTITY()

        WHILE @k < 10
        BEGIN
            INSERT INTO [dbo].[table3] ([Table2Id]) VALUES (@table2Id)
            SELECT @table3Id = SCOPE_IDENTITY()

            WHILE @l > 0
            BEGIN
                INSERT INTO [dbo].[table4] ([Table3Id]) VALUES (@table3Id)
                SELECT @table4Id = SCOPE_IDENTITY()

                WHILE @m < 10
                BEGIN
                    INSERT INTO [dbo].[table5] ([Table4Id], [Description]) VALUES (@table4Id, 'Not so long description')

                    SET @m = @m + 1;
                END;

                SET @m = 0;
                SET @l = @l - 1;
            END;

            SET @l = 10;
            SET @k = @k + 1;
        END;

        SET @k = 0;
        SET @j = @j + 1;
    END;

    SET @j = 0;
    SET @i = @i + 1;
END;
commit
sql-server index
  • 3 3 respostas
  • 2000 Views

3 respostas

  • Voted
  1. Best Answer
    Joe Obbish
    2016-12-22T08:42:33+08:002016-12-22T08:42:33+08:00

    Primeiro, vamos obter uma estimativa do número de leituras necessárias para fazer uma varredura de índice para IdxTable5_FKTable4Id:

    -- get an idea of number of pages in the index
    SELECT *
    FROM sys.dm_db_partition_stats s
    WHERE OBJECT_NAME(s.object_id) IN ('table5')
    AND index_id > 1;
    

    No meu sistema, os resultados dessa consulta sugerem que o SQL Server precisará de cerca de 900 leituras para ler o índice por completo. Para testar isso, executarei uma consulta simples que é implementada com mais eficiência como uma varredura de índice no SQL Server. A consulta abaixo precisa da coluna de ID para todas as linhas da tabela5. O SQL Server pode apenas consultar o índice para obter todos os dados necessários para a consulta. Como cada linha é necessária, não há trabalho desperdiçado aqui.

    -- get logical reads after query execution
    SET STATISTICS IO ON;
    
    select t5.Id
    from table5 t5;
    

    Tabela 'mesa5'. Contagem de varredura 1, leituras lógicas 900, leituras físicas 0, leituras antecipadas 0, leituras lógicas lob 0, leituras físicas lob 0, leituras antecipadas lob 0.

    Agora vamos considerar sua primeira consulta de teste que não usa a dica. Estima-se que a tabela externa para a correspondência de hash final tenha 9657 linhas em meu sistema. O otimizador de consulta decidiu que uma varredura de índice em IdxTable5_FKTable4Id é um plano bom o suficiente. Aqui está a consulta que eu executei:

    select      t5.Id as t5Id
    from        table1 t1
    left join   table2 t2 on t2.Table1Id = t1.Id
    left join   table3 t3 on t3.Table2Id = t2.Id
    left join   table4 t4 on t4.Table3Id = t3.Id
    left join   table5 t5 on t5.Table4Id = t4.Id;
    

    Tabela 'mesa5'. Contagem de varredura 1, leituras lógicas 900, leituras físicas 0, leituras antecipadas 0, leituras lógicas lob 0, leituras físicas lob 0, leituras antecipadas lob 0.

    A tabela externa para o hash join na verdade tinha 10.000 linhas. No entanto, como essa é uma junção de hash, o SQL Server ainda precisava verificar todas as 100.000 linhas do índice, embora apenas 10.000 fossem necessárias. É por isso que as mesmas 900 leituras lógicas são necessárias para esta consulta como a primeira. Pode-se argumentar que isso é um esforço desperdiçado pelo otimizador de consulta. Poderia ser mais eficiente usar uma busca de índice para buscar apenas as 10.000 linhas necessárias do índice?

    Primeiro vamos obter uma estimativa para o número de leituras necessárias. Seu índice tem uma profundidade de 3:

    -- get the index depth of the index
    SELECT INDEXPROPERTY ( object_ID('table5'), 'IdxTable5_FKTable4Id' , 'IndexDepth');
    

    Também permitirei que o TF 8744 obtenha resultados mais limpos para o propósito desta demonstração:

    -- Trace flag 8744: Disable pre-fetching for ranges
    dbcc traceon(8744);
    

    Eu sei que existem 10.000 linhas da tabela externa, então uma estimativa para o número de leituras lógicas é 10.000 * (3 + 1) = 40.000. Por linha, são 3 para a profundidade do índice e 1 para obter os dados.

    Aqui está a consulta que foi testada:

    select      t5.Id as t5Id
    from        table1 t1
    left join   table2 t2 on t2.Table1Id = t1.Id
    left join   table3 t3 on t3.Table2Id = t2.Id
    left join   table4 t4 on t4.Table3Id = t3.Id
    left join   table5 t5 WITH (FORCESEEK) on t5.Table4Id = t4.Id;
    

    Tabela 'mesa5'. Contagem de varredura 10000, leituras lógicas 41118, leituras físicas 0, leituras antecipadas 0, leituras lógicas lob 0, leituras físicas lob 0, leituras antecipadas lob 0.

    Isso é bem próximo da estimativa de 40.000.

    O que aprendemos aqui? Para esse índice, há um custo de cerca de 4 leituras por busca de índice e um custo fixo de 900 leituras para fazer a varredura. Isso significa que, de uma perspectiva de IO, o uso de buscas de índice só será mais eficiente ao obter uma pequena porcentagem dos dados do índice. Caso contrário, obter todos os dados usando uma varredura de índice, mesmo que não seja necessário para retornar resultados corretos para a consulta, será mais eficiente.

    Para um teste final, vamos tentar recuperar as primeiras 1.000 linhas da consulta de teste original. No meu sistema, o otimizador de consulta escolhe naturalmente uma busca de índice, mesmo sem a dica. Aqui está a consulta que eu executei:

    select TOP (1000)    t5.Id as t5Id
    from        table1 t1
    left join   table2 t2 on t2.Table1Id = t1.Id
    left join   table3 t3 on t3.Table2Id = t2.Id
    left join   table4 t4 on t4.Table3Id = t3.Id
    left join   table5 t5 on t5.Table4Id = t4.Id;
    

    Tabela 'mesa5'. Contagem de varredura 100, leituras lógicas 409, leituras físicas 0, leituras antecipadas 0, leituras lógicas lob 0, leituras físicas lob 0, leituras antecipadas lob 0.

    Para essa consulta, as buscas de índice podem ser vistas como mais eficientes do que uma varredura de índice. Observe que a tabela externa tem 100 linhas, então 100 buscas foram feitas. Uma busca pode retornar mais de 1 linha. É por isso que as leituras lógicas estão próximas de 400 em vez de 4000.

    • 7
  2. DaSiHu
    2016-12-22T03:26:46+08:002016-12-22T03:26:46+08:00

    Há muito, muito mais do que uma simples questão de "digitalizar versus buscar". O SQL pode funcionar bem ou mal, dependendo de outros fatores. Uma busca pode ser executada 900 vezes retornando uma linha de cada vez, enquanto uma varredura pode retornar todas as 900 linhas em uma visita. A varredura é melhor nesse cenário.

    Você deve olhar para o plano de execução e se familiarizar com a forma de lê-los.

    https://stackoverflow.com/questions/758912/how-to-read-an-execution-plan-in-sql-server

    • 0
  3. Robert Sievers
    2018-07-25T11:31:48+08:002018-07-25T11:31:48+08:00

    Eu vi a mesma coisa em algumas otimizações que estava fazendo. A criação de alguns índices de cobertura acelerou a consulta, mas resultou em um aumento de cerca de 10 vezes nas leituras lógicas.

    Há um artigo muito bom sobre isso em http://www.dbsophic.com/learn-more/sql-server-articles/53-tip-comparing-db-sql-server-logical-reads-what-they-really -dizer

    De acordo com Ami Levin, o motivo é que um índice bem criado resulta em uma busca de índice com junções de loop aninhadas, em vez de varreduras de índice com junções de hash. Aparentemente, o SQL não conta os testes de hash como leituras lógicas, o que significa que as leituras lógicas entre duas formas de plano diferentes podem realmente comparar maçãs e laranjas.

    • 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

    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