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 / 180461
Accepted
Matthew McGiffen
Matthew McGiffen
Asked: 2017-07-22 06:46:49 +0800 CST2017-07-22 06:46:49 +0800 CST 2017-07-22 06:46:49 +0800 CST

Comportamento estranho com tamanhos de amostra para atualizações de estatísticas

  • 772

Andei investigando limites de amostragem com atualizações de estatísticas no SQL Server (2012) e notei um comportamento curioso. Basicamente, o número de linhas amostradas parece variar em algumas circunstâncias - mesmo com o mesmo conjunto de dados.

Eu executo esta consulta:

--Drop table if exists
IF (OBJECT_ID('dbo.Test')) IS NOT NULL DROP TABLE dbo.Test;

--Create Table for Testing
CREATE TABLE dbo.Test(Id INT IDENTITY(1,1) CONSTRAINT PK_Test PRIMARY KEY CLUSTERED, TextValue VARCHAR(20) NULL);

--Insert enough data so we have more than 8Mb (the threshold at which sampling kicks in)
INSERT INTO dbo.Test(TextValue) 
SELECT TOP 1000000 'blahblahblah'
FROM sys.objects a, sys.objects b, sys.objects c, sys.objects d;  

--Create Index on TextValue
CREATE INDEX IX_Test_TextValue ON dbo.Test(TextValue);

--Update Statistics without specifying how many rows to sample
UPDATE STATISTICS dbo.Test IX_Test_TextValue;

--View the Statistics
DBCC SHOW_STATISTICS('dbo.Test', IX_Test_TextValue) WITH STAT_HEADER;

Quando olho para a saída do SHOW_STATISTICS, estou descobrindo que o "Rows Sampled" varia com cada execução completa (ou seja, a tabela é descartada, recriada e repovoada).

Por exemplo:

Linhas amostradas

  • 318618
  • 319240
  • 324198
  • 314154

Minha expectativa era que esse valor fosse o mesmo toda vez que a tabela fosse idêntica. A propósito, não recebo esse comportamento se apenas excluir os dados e reinserir.

Não é uma pergunta crítica, mas eu estaria interessado em entender o que está acontecendo.

sql-server sql-server-2012
  • 3 3 respostas
  • 718 Views

3 respostas

  • Voted
  1. Best Answer
    Paul White
    2017-07-26T09:03:06+08:002017-07-26T09:03:06+08:00

    Fundo

    Os dados para o objeto de estatísticas são coletados usando uma declaração do formulário:

    SELECT 
        StatMan([SC0], [SC1], [SB0000]) 
    FROM 
    (
        SELECT TOP 100 PERCENT 
            [SC0], [SC1], STEP_DIRECTION([SC0]) OVER (ORDER BY NULL) AS [SB0000]
        FROM 
        (
            SELECT 
                [TextValue] AS [SC0], 
                [Id] AS [SC1] 
            FROM [dbo].[Test] 
                TABLESAMPLE SYSTEM (2.223684e+001 PERCENT) 
                WITH (READUNCOMMITTED) 
        ) AS _MS_UPDSTATS_TBL_HELPER 
        ORDER BY 
            [SC0], 
            [SC1], 
            [SB0000] 
    ) AS _MS_UPDSTATS_TBL
    OPTION (MAXDOP 1)
    

    Você pode coletar essa declaração com Extended Events ou Profiler ( SP:StmtCompleted).

    As consultas de geração de estatísticas geralmente acessam a tabela base (em vez de um índice não clusterizado) para evitar o agrupamento de valores que ocorre naturalmente em páginas de índice não clusterizadas.

    O número de linhas amostradas depende do número de páginas inteiras selecionadas para amostragem. Cada página da tabela está selecionada ou não. Todas as linhas nas páginas selecionadas contribuem para as estatísticas.

    Números aleatórios

    O SQL Server usa um gerador de números aleatórios para decidir se uma página se qualifica ou não. O gerador usado nesta instância é o gerador de números aleatórios Lehmer com valores de parâmetros conforme mostrado abaixo:

    X próximo = X semente * 7 5 mod (2 31 - 1)
    

    O valor de é calculado como a soma de:Xseed

    • A parte inteira baixa da biginttabela base ( ), partition_idpor exemplo

      SELECT
          P.[partition_id] & 0xFFFFFFFF
      FROM sys.partitions AS P
      WHERE
          P.[object_id] = OBJECT_ID(N'dbo.Test', N'U')
          AND P.index_id = 1;
      
    • O valor especificado na REPEATABLEcláusula

      • Para amostrado UPDATE STATISTICS, o REPEATABLEvalor é 1.
      • Este valor é exposto no m_randomSeedelemento das informações de depuração interna do método de acesso mostradas nos planos de execução quando o sinalizador de rastreamento 8666 está habilitado, por exemplo<Field FieldName="m_randomSeed" FieldValue="1" />

    Para o SQL Server 2012, esse cálculo ocorre em sqlmin!UnOrderPageScanner::StartScan:

    mov     edx,dword ptr [rcx+30h]
    add     edx,dword ptr [rcx+2Ch]
    

    onde memory at [rcx+30h]contém os 32 bits mais baixos do id da partição e memory at [rcx+2Ch]contém o REPEATABLEvalor em uso.

    O gerador de números aleatórios é inicializado posteriormente no mesmo método, chamando sqlmin!RandomNumGenerator::Init, onde a instrução:

    imul    r9d,r9d,41A7h
    

    ...multiplica a semente por 41A7hexadecimal (16807 decimal = 7 5 ) como mostrado na equação acima.

    Números aleatórios posteriores (para páginas individuais) são gerados usando o mesmo código básico embutido em sqlmin!UnOrderPageScanner::SetupSubScanner.

    StatMan

    Para a StatManconsulta de exemplo mostrada acima, as mesmas páginas serão coletadas para a instrução T-SQL:

    SELECT 
        COUNT_BIG(*) 
    FROM dbo.Test AS T 
        TABLESAMPLE SYSTEM (2.223684e+001 PERCENT)  -- Same sample %
        REPEATABLE (1)                              -- Always 1 for statman
        WITH (INDEX(0));                            -- Scan base object
    

    Isso corresponderá à saída de:

    SELECT 
        DDSP.rows_sampled
    FROM sys.stats AS S
    CROSS APPLY sys.dm_db_stats_properties(S.[object_id], S.stats_id) AS DDSP
    WHERE 
        S.[object_id] = OBJECT_ID(N'dbo.Test', N'U')
        AND S.[name] = N'IX_Test_TextValue';
    

    Caso de borda

    Uma consequência do uso do gerador de números aleatórios MINSTD Lehmer é que os valores de semente zero e int.max não devem ser usados, pois isso fará com que o algoritmo produza uma sequência de zeros (selecionando cada página).

    O código detecta zero e usa um valor do 'relógio' do sistema como a semente nesse caso. Não faz o mesmo se a semente for int.max ( 0x7FFFFFFF= 2 31 - 1).

    Podemos projetar esse cenário, pois a semente inicial é calculada como a soma dos 32 bits mais baixos do ID da partição e do REPEATABLEvalor. O REPEATABLEvalor que resultará na semente sendo int.max e, portanto, em cada página selecionada para amostra é:

    SELECT
        0x7FFFFFFF - (P.[partition_id] & 0xFFFFFFFF)
    FROM sys.partitions AS P
    WHERE
        P.[object_id] = OBJECT_ID(N'dbo.Test', N'U')
        AND P.index_id = 1;
    

    Trabalhando isso em um exemplo completo:

    DECLARE @SQL nvarchar(4000) = 
        N'
        SELECT
            COUNT_BIG(*) 
        FROM dbo.Test AS T 
            TABLESAMPLE (0 PERCENT) 
            REPEATABLE (' +
            (
                SELECT TOP (1)
                    CONVERT(nvarchar(11), 0x7FFFFFFF - P.[partition_id] & 0xFFFFFFFF)
                FROM sys.partitions AS P
                WHERE
                    P.[object_id] = OBJECT_ID(N'dbo.Test', N'U')
                    AND P.index_id = 1
            ) + ')
            WITH (INDEX(0));';
    
    PRINT @SQL;
    --EXECUTE (@SQL);
    

    Isso selecionará todas as linhas em todas as páginas, independentemente da TABLESAMPLEcláusula (mesmo zero por cento).

    • 27
  2. Joe Obbish
    2017-07-22T09:09:31+08:002017-07-22T09:09:31+08:00

    Essa é uma excelente pergunta! Vou começar com o que sei com certeza e depois passar para a especulação. Muitos detalhes sobre isso no meu post aqui .

    Atualizações de estatísticas de amostra usadas TABLESAMPLEnos bastidores. É muito fácil encontrar documentação sobre isso online. No entanto, acredito que não seja bem conhecido que as linhas retornadas por TABLESAMPLEdependem parcialmente hobt_iddo objeto. Quando você descarta e recria o objeto, obtém um novo hobt_idpara que as linhas retornadas por amostragem aleatória sejam diferentes.

    Se você excluir e reinserir os dados, eles hobt_idpermanecem os mesmos. Contanto que os dados sejam dispostos da mesma maneira no disco (uma varredura de ordem de alocação retorna os mesmos resultados na mesma ordem), os dados amostrados não devem ser alterados.

    Você também pode alterar o número de linhas amostradas recriando o índice clusterizado na tabela. Por exemplo:

    UPDATE STATISTICS dbo.Test IX_Test_TextValue;
    
    DBCC SHOW_STATISTICS('dbo.Test', IX_Test_TextValue) WITH STAT_HEADER; -- 273862 rows
    
    ALTER INDEX PK_Test on Test REBUILD;
    
    UPDATE STATISTICS dbo.Test IX_Test_TextValue;
    
    DBCC SHOW_STATISTICS('dbo.Test', IX_Test_TextValue) WITH STAT_HEADER; -- 273320 rows
    

    Por que isso acontece, acredito que seja porque o SQL Server verifica o índice clusterizado em vez do índice não clusterizado ao coletar estatísticas de amostra em um índice. Eu também acho que há um valor oculto (para aqueles de nós que rastreiam as consultas de atualização de estatísticas ocultas) para REPEATABLEusado com TABLESAMPLE. Eu não provei nada disso, mas explica por que seu histograma e as linhas amostradas mudam com uma reconstrução do índice clusterizado.

    • 11
  3. sepupic
    2017-07-25T05:49:27+08:002017-07-25T05:49:27+08:00

    Esqueci como TABLESAMPLE funcionava em termos de atribuição de uma probabilidade aleatória por página. - Martin Smith

    Eu vi isso em Inside Microsoft SQL Server 2008: T-SQL Querying de Itzik Ben-Gan e não posso adicioná-lo como comentário, então posto aqui, acho que é interessante para outros também:

    insira a descrição da imagem aqui

    Veja também Amostragem Usando TABLESAMPLE de Roji. P. Thomas.

    • 3

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