SELECIONE O TOP 1 de uma tabela muito grande em uma coluna de índice é muito lento, mas não com ordem inversa ("desc")

16

Temos um banco de dados grande, de cerca de 1 TB, executando o SQL Server 2014 em um servidor poderoso. Tudo funcionou bem por alguns anos. Cerca de duas semanas atrás, fizemos uma manutenção completa, que incluía: Instale todas as atualizações de software; reconstruir todos os índices e compactar arquivos de banco de dados. No entanto, não esperávamos que, em determinado estágio, o uso da CPU do DB aumentasse de 100% a 150% quando a carga real fosse a mesma.

Após muita solução de problemas, reduzimos para uma consulta muito simples, mas não conseguimos encontrar uma solução. A consulta é extremamente simples:

select top 1 EventID from EventLog with (nolock) order by EventID

Leva sempre cerca de 1,5 segundos! No entanto, uma consulta semelhante com "desc" sempre leva cerca de 0 ms:

select top 1 EventID from EventLog with (nolock) order by EventID desc

PTable possui cerca de 500 milhões de linhas; EventIDé a coluna principal do índice em cluster (ordenada ASC) com o tipo de dados bigint (coluna Identidade). Existem vários threads inserindo dados na tabela na parte superior (EventIDs maiores) e há 1 thread excluindo dados da parte inferior (EventIDs menores).

No SMSS, verificamos que as duas consultas sempre usam o mesmo plano de execução:

  • Varredura de índice em cluster;

  • Os números de linha estimados e reais são ambos 1;

  • O número estimado e real de execuções é 1;

  • O custo estimado de E / S é 8500 (parece alto)

  • Se executado consecutivamente, o custo da consulta é o mesmo 50% para ambos.

Atualizei as estatísticas do índice with fullscan, o problema persistiu; Reconstruí o índice novamente, e o problema parecia ter desaparecido por meio dia, mas voltou.

Ativei as estatísticas de IO com:

set statistics io on

em seguida, executou as duas consultas consecutivamente e encontrou as seguintes informações:

(Para a primeira consulta, a lenta)

Tabela 'PTable'. Contagem de varredura 1, leituras lógicas 407670, leituras físicas 0, leituras de read-ahead 0, leituras lógicas de lob 0, leituras físicas de lob 0, leituras físicas de lob 0, leituras de read-ahead de lob 0.

(Para a segunda consulta, a mais rápida)

Tabela 'PTable'. Contagem de varredura 1, leituras lógicas 4, leituras físicas 0, leituras de read-ahead 0, leituras lógicas de lob 0, leituras físicas de lob 0, leituras físicas de lob 0, leituras de read-ahead de lob 0.

Observe a enorme diferença nas leituras lógicas. O índice é usado nos dois casos.

A fragmentação do índice pode explicar um pouco, mas acredito que o impacto é muito pequeno; e o problema nunca aconteceu antes. Outra prova é se eu executar uma consulta como:

select * from EventLog with (nolock) where EventID=xxxx   

Mesmo se eu definir xxxx como os menores EventIDs da tabela, a consulta será sempre muito rápida.

Verificamos e não há nenhum problema de bloqueio / bloqueio.

Nota: Eu apenas tentei simplificar o problema acima. O "PTable" é realmente "EventLog"; o PIDé EventID.

Eu obter o mesmo teste resultado sem a NOLOCKdica.

Alguém pode ajudar?

insira a descrição da imagem aqui

insira a descrição da imagem aqui

Planos de execução de consulta mais detalhados em XML, da seguinte maneira:

https://www.brentozar.com/pastetheplan/?id=SJ3eiVnob

https://www.brentozar.com/pastetheplan/?id=r1rOjVhoZ

Eu não acho que seja importante fornecer a instrução create table. É um banco de dados antigo e está funcionando perfeitamente há muito tempo até a manutenção. Fizemos muitas pesquisas e reduzimos as informações fornecidas na minha pergunta.

A tabela foi criada normalmente com a EventIDcoluna como chave primária, que é uma identitycoluna do tipo bigint. No momento, acho que o problema está na fragmentação do índice. Logo após a reconstrução do índice, o problema parecia ter desaparecido por meio dia; mas por que voltou tão rápido ...?

TiffanyP
fonte

Respostas:

17

A Varredura de Índice em Cluster mostra 423.723 leituras lógicas para retornar a primeira linha, levando 1926 ms:

NUTS

Isso parece bastante para localizar a primeira linha na ordem do índice.

Provavelmente sua tarefa de limpeza de fantasmas está muito atrasada ou parou. Você deve verificar o ghost_record_countíndice em cluster sys.dm_db_index_physical_statse monitorar as alterações ao longo do tempo.

A varredura ordenada do final do índice que está vendo a atividade de exclusão constante precisa varrer uma enorme quantidade de registros fantasmas antes de encontrar a primeira linha 'ativa' a retornar. Isso explica as leituras lógicas extras. Uma busca na árvore b até o valor mais baixo do índice encontrará muito menos registros fantasmas.

Outro fator que afeta o desempenho é que a própria varredura se torna responsável por remover os registros fantasmas, conforme mencionado em Por dentro da limpeza do mecanismo de armazenamento: Ghost por Paul Randal.

Você deve verificar se o sinalizador de rastreamento 661 (desativar a limpeza fantasma) não está ativo.

Soluções

Se o processo de limpeza fantasma foi interrompido completamente, a solução mais eficaz é normalmente reiniciar a instância do SQL Server. Você também deve garantir que o SQL Server esteja executando uma das atualizações cumulativas mais recentes. Houve muitos erros de limpeza de fantasmas ao longo dos anos.

No seu caso específico:

Acontece que o problema foi causado por outro banco de dados de teste no mesmo servidor. Esse banco de dados de teste foi restaurado com "perda de dados" e está corrompido. Surpreendentemente, o processo de limpeza de fantasmas aparentemente ficou preso nesse banco de dados. Depois que excluímos o banco de dados corrompido do SMSS, o problema foi resolvido por si só (demorou muito tempo e pode ter causado o bloqueio do DB por um tempo).

Paul White restabelece Monica
fonte