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 / 116368
Accepted
Geoff Patterson
Geoff Patterson
Asked: 2015-09-29 10:44:21 +0800 CST2015-09-29 10:44:21 +0800 CST 2015-09-29 10:44:21 +0800 CST

Estimativa de cardinalidade ruim desqualifica INSERT do log mínimo?

  • 772

Por que a segunda INSERTinstrução é aproximadamente 5x mais lenta que a primeira?

Pela quantidade de dados de log gerados, acho que o segundo não se qualifica para o registro mínimo. No entanto, a documentação no Guia de desempenho de carregamento de dados indica que ambas as inserções devem poder ser minimamente registradas. Portanto, se o log mínimo é a principal diferença de desempenho, por que a segunda consulta não se qualifica para o log mínimo? O que pode ser feito para melhorar a situação?


Consulta nº 1: Inserindo linhas de 5MM usando INSERT...WITH (TABLOCK)

Considere a consulta a seguir, que insere 5MM de linhas em um heap. Esta consulta é executada 1 seconde gerada 64MBpor dados de log de transações, conforme relatado por sys.dm_tran_database_transactions.

CREATE TABLE dbo.minimalLoggingTest (n INT NOT NULL)
GO
INSERT INTO dbo.minimalLoggingTest WITH (TABLOCK) (n)
SELECT n
-- Any table/view/sub-query that correctly estimates that it will generate 5MM rows
FROM dbo.fiveMillionNumbers
-- Provides greater consistency on my laptop, where other processes are running
OPTION (MAXDOP 1)
GO


Consulta nº 2: Inserindo os mesmos dados, mas o SQL subestima o número de linhas

Agora, considere esta consulta muito semelhante, que opera exatamente com os mesmos dados, mas extrai de uma tabela (ou SELECTinstrução complexa com muitas junções em meu caso de produção real) onde a estimativa de cardinalidade é muito baixa. Esta consulta executa 5.5 secondse gera 461MBdados de log de transações.

CREATE TABLE dbo.minimalLoggingTest (n INT NOT NULL)
GO
INSERT INTO dbo.minimalLoggingTest WITH (TABLOCK) (n)
SELECT n
-- Any table/view/sub-query that produces 5MM rows but SQL estimates just 1000 rows
FROM dbo.fiveMillionNumbersBadEstimate
-- Provides greater consistency on my laptop, where other processes are running
OPTION (MAXDOP 1)
GO


roteiro completo

Consulte este Pastebin para obter um conjunto completo de scripts para gerar os dados de teste e executar qualquer um desses cenários. Observe que você deve usar um banco de dados que esteja no SIMPLE modelo de recuperação .


Contexto empresarial

Estamos movendo com frequência milhões de linhas de dados e é importante que essas operações sejam o mais eficientes possível, tanto em termos de tempo de execução quanto de carga de E/S do disco. Inicialmente, tínhamos a impressão de que criar uma tabela heap e usar INSERT...WITH (TABLOCK)era uma boa maneira de fazer isso, mas agora ficamos menos confiantes, pois observamos a situação demonstrada acima em um cenário de produção real (embora com consultas mais complexas, não o versão simplificada aqui).

sql-server performance
  • 3 3 respostas
  • 375 Views

3 respostas

  • Voted
  1. Best Answer
    Paul White
    2015-09-30T21:49:44+08:002015-09-30T21:49:44+08:00

    Por que a segunda consulta não se qualifica para registro mínimo?

    O log mínimo está disponível para a segunda consulta, mas o mecanismo opta por não usá-lo no tempo de execução.

    Há um limite mínimo abaixo do INSERT...SELECTqual ele escolhe não usar as otimizações de carregamento em massa. Há um custo envolvido na configuração de uma operação de conjunto de linhas em massa e a inserção em massa de apenas algumas linhas não resultaria na utilização eficiente do espaço.

    O que pode ser feito para melhorar a situação?

    Use um dos muitos outros métodos (por exemplo SELECT INTO) que não possui esse limite. Como alternativa, você pode reescrever a consulta de origem de alguma forma para aumentar o número estimado de linhas/páginas acima do limite para INSERT...SELECT.

    Veja também a auto-resposta de Geoff para informações mais úteis.


    Curiosidades possivelmente interessantes: SET STATISTICS IO relata leituras lógicas para a tabela de destino somente quando as otimizações de carregamento em massa não são usadas .

    • 7
  2. Hannah Vernon
    2015-09-29T11:36:32+08:002015-09-29T11:36:32+08:00

    Consegui recriar o problema com meu próprio equipamento de teste:

    USE test;
    
    CREATE TABLE dbo.SourceGood
    (
        SourceGoodID INT NOT NULL
            CONSTRAINT PK_SourceGood
            PRIMARY KEY CLUSTERED
            IDENTITY(1,1)
        , SomeData VARCHAR(384) NOT NULL
    );
    
    CREATE TABLE dbo.SourceBad
    (
        SourceBadID INT NOT NULL
            CONSTRAINT PK_SourceBad
            PRIMARY KEY CLUSTERED
            IDENTITY(-2147483647,1)
        , SomeData VARCHAR(384) NOT NULL
    );
    
    CREATE TABLE dbo.InsertTest
    (
        SourceBadID INT NOT NULL
            CONSTRAINT PK_InsertTest
            PRIMARY KEY CLUSTERED
        , SomeData VARCHAR(384) NOT NULL
    );
    GO
    
    INSERT INTO dbo.SourceGood WITH (TABLOCK) (SomeData) 
    SELECT TOP(5000000) o.name + o1.name + o2.name
    FROM syscolumns o
        , syscolumns o1
        , syscolumns o2;
    GO
    
    ALTER DATABASE test SET AUTO_UPDATE_STATISTICS OFF;
    GO
    
    INSERT INTO dbo.SourceBad WITH (TABLOCK) (SomeData)
    SELECT TOP(5000000) o.name + o1.name + o2.name
    FROM syscolumns o
        , syscolumns o1
        , syscolumns o2;
    GO
    
    ALTER DATABASE test SET AUTO_UPDATE_STATISTICS ON;
    GO
    
    BEGIN TRANSACTION;
    
    INSERT INTO dbo.InsertTest WITH (TABLOCK)
    SELECT *
    FROM dbo.SourceGood;
    
    SELECT * FROM sys.dm_tran_database_transactions;
    
    /*
    database_transaction_log_record_count
    472 
    database_transaction_log_bytes_used
    692136
    */
    
    COMMIT TRANSACTION;
    
    
    BEGIN TRANSACTION;
    
    INSERT INTO dbo.InsertTest WITH (TABLOCK)
    SELECT *
    FROM dbo.SourceBad;
    
    SELECT * FROM sys.dm_tran_database_transactions;
    
    /*
    database_transaction_log_record_count   
    5000003 
    database_transaction_log_bytes_used
    642699256
    */
    
    COMMIT TRANSACTION;
    

    Isso levanta a questão: por que não "corrigir" o problema atualizando as estatísticas nas tabelas de origem antes de executar a operação minimamente registrada?

    TRUNCATE TABLE dbo.InsertTest;
    UPDATE STATISTICS dbo.SourceBad;
    
    BEGIN TRANSACTION;
    
    INSERT INTO dbo.InsertTest WITH (TABLOCK)
    SELECT *
    FROM dbo.SourceBad;
    
    SELECT * FROM sys.dm_tran_database_transactions;
    
    /*
    database_transaction_log_record_count
    472
    database_transaction_log_bytes_used
    692136
    */
    
    COMMIT TRANSACTION;
    
    • 5
  3. Geoff Patterson
    2015-10-06T13:28:20+08:002015-10-06T13:28:20+08:00

    Reescreva a consulta de origem de alguma forma para aumentar o número estimado de linhas

    Expandindo a ideia de Paul, uma solução alternativa se você estiver realmente desesperado é adicionar uma tabela fictícia que garanta que o número estimado de linhas para a inserção seja alto o suficiente para qualidade para otimizações de carregamento em massa. Confirmei que isso obtém um registro mínimo e melhora o desempenho da consulta.

    -- Create a dummy table that SQL Server thinks has a million rows
    CREATE TABLE dbo.emptyTableWithMillionRowEstimate (
        n INT PRIMARY KEY
    )
    GO
    UPDATE STATISTICS dbo.emptyTableWithMillionRowEstimate
    WITH ROWCOUNT = 1000000
    GO
    
    -- Concatenate this table into the final rowset:
    INSERT INTO dbo.minimalLoggingTest WITH (TABLOCK) (n)
    SELECT n
    -- Any table/view/sub-query that correctly estimates that it will generate 5MM rows
    FROM dbo.fiveMillionNumbersBadEstimate
    -- Add in dummy rowset to ensure row estimate is high enough for bulk load optimization
    UNION ALL
    SELECT NULL FROM dbo.emptyTableWithMillionRowEstimate
    OPTION (MAXDOP 1)
    

    Considerações finais

    1. Use SELECT...INTOpara operações de inserção únicas se for necessário registrar minimamente. Como Paul aponta, isso garantirá o registro mínimo, independentemente da estimativa de linha
    2. Sempre que possível, escreva consultas de maneira simples que o otimizador de consulta possa raciocinar com eficiência. Pode ser possível dividir uma consulta em várias partes, por exemplo, para permitir que as estatísticas sejam construídas em uma tabela intermediária.
    3. Se você tiver acesso ao SQL Server 2014, experimente em sua consulta; no meu caso de produção real, acabei de experimentá-lo e o novo Estimador de Cardinalidade produziu uma estimativa muito maior (e melhor); a consulta então foi minimamente registrada. Mas isso pode não ser útil se você precisar oferecer suporte ao SQL 2012 e anteriores.
    4. Se você estiver desesperado, soluções hacky como esta podem ser aplicadas!

    Um artigo relacionado

    A postagem no blog de maio de 2019 de Paul White Registro mínimo com INSERT…SELECT em tabelas heap aborda algumas dessas informações com mais detalhes.

    • 4

relate perguntas

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

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

  • Onde posso encontrar o log lento do mysql?

  • Como posso otimizar um mysqldump de um banco de dados grande?

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