Uma má combinação – SQLBlog

Uma má combinação – SQLBlog

Uma má combinação - SQLBlog 1
cupom com desconto - o melhor site de cupom de desconto cupomcomdesconto.com.br


Uma de nossas equipes realiza periodicamente uma consulta agregada em meia dúzia de tabelas, de tamanhos variados, em quatro bancos de dados. Uma das tabelas não ficará permanentemente; é um pequeno heap (300 mil linhas, 10 MB) com uma coluna LOB (nvarchar(max)) sendo usado temporariamente para filtrar SKUs.

De vez em quando, a consulta causa esse desagradável despejo de pilha:

Local: lobss.cpp: 725
Expressão: 0
SPID: 420
ID do processo: 22984
Descrição: nunca deve acontecer

Msg 3624, Nível 20, Estado 1, Linha 5
Uma verificação de asserção do sistema falhou. Verifique o log de erros do SQL Server para obter detalhes. Normalmente, uma falha de declaração é causada por um erro de software ou corrupção de dados. Para verificar a corrupção do banco de dados, considere executar o DBCC CHECKDB. Se você concordou em enviar despejos para a Microsoft durante a instalação, um mini despejo será enviado à Microsoft. Uma atualização pode estar disponível na Microsoft no Service Pack mais recente ou em um hotfix do Suporte técnico.

Mensagem 596, nível 21, estado 1, linha 0
Não é possível continuar a execução porque a sessão está no estado de interrupção.

Msg 0, Nível 20, Estado 0, Linha 0
Um erro severo ocorreu no comando atual. Os resultados, se existirem, deveriam ser descartados.

Isto não é bom.

Eu li a software bug or data corruption e depois CHECKDB encolheu os ombros em todos os bancos de dados relevantes, descartei a corrupção. Além disso, o problema é intermitente; se houve corrupção causadora de exceção em uma página que é lida por uma consulta, a exceção deve ocorrer toda vez que a consulta é executada (e deve ser notada por verificações lógicas).

Então, vamos arregaçar as mangas.

Enquanto o errorlog tem algumas informações sobre o despejo e o arquivo .mdmp tem todos os detalhes, tudo o que você realmente precisa é SQLDump0nnn.txt. Para uma análise profunda, você provavelmente desejará realmente carregar o .mdmp arquivo no WinDbg. Mas o WinDbg é tedioso. O Arvind Shyamsundar da Microsoft mantém incansavelmente o SQLCallStackResolver, bem como os scripts do PowerShell para baixar os arquivos de símbolos corretos (a parte mais frustrante do uso do WinDbg IMHO).

Leia Também  Azure Database for MySQL

Os arquivos .mdmp e complementares são encontrados no diretório errorlog do SQL Server, que você pode analisar:

SELECT SERVERPROPERTY(N'ErrorLogFileName');

Se houver mais de um conjunto de despejos, será necessário correlacionar os carimbos de data e hora do arquivo com as entradas do log de erros para garantir que você esteja analisando o correto.

Chato, Aaron. Para a lixeira, já!

Eu poderia ter encaminhado tudo para o suporte da Microsoft e ainda pretendo fazer isso adequadamente esta semana, pois ele precisa ser investigado por eles para obter uma correção real do produto. Mas é definitivamente divertido soletrar por conta própria nesse meio tempo. Fiz o download dos símbolos para a compilação relevante do SQL Server 2017 e colei a pilha de chamadas do arquivo de texto no aplicativo.

À esquerda faz parte do despejo bruto e à direita (bem, a menos que sua tela seja pequena) é a pilha de chamadas simbolizada, correspondente linha por linha, com destaque relevante, cortesia de mim:

Módulo 00007FFBA0E685BC (KERNELBASE + 00000000000085BC)
Módulo 00007FFB6C43125E (sqllang + 0000000000FB125E)
Módulo 00007FFB6C43516D (sqllang + 0000000000FB516D)
Módulo 00007FFB6C479EAF (sqllang + 0000000000FF9EAF)
Módulo 00007FFB83D03694 (sqldk + 0000000000113694)
Módulo 00007FFB806DE1CF (sqlmin + 000000000187E1CF)
Módulo 00007FFB806DE0C1 (sqlmin + 000000000187E0C1)
Módulo 00007FFB7F7B54F5 (sqlmin + 00000000009554F5)
Módulo 00007FFB7F7B2684 (sqlmin + 0000000000952684)
Módulo 00007FFB7F7B1D6B (sqlmin + 0000000000951D6B)
Módulo 00007FFB7F743042 (sqlmin + 00000000008E3042)
Módulo 00007FFB848227C7 (sqlTsEs + 00000000000027C7)
Módulo 00007FFB7F7947D6 (sqlmin + 00000000009347D6)
Módulo 00007FFB7F794718 (sqlmin + 0000000000934718)

cupom com desconto - o melhor site de cupom de desconto cupomcomdesconto.com.br

sqllang! CStackDump :: GetContextAndDump
sqllang! stackTrace
sqllang! utassert_fail_imp
sqldk! utassert_fail
sqlmin! LobStorageProvider :: RaiseInternalError
sqlmin! LobStorageProvider ::GetLobValueForRead
sqlmin! CBlobHandleFactoryMain :: PilbGetILockBytesInternal
sqlmin! CBlobHandleHelper ::CopyBlobHandleIntoTempOrInlined
sqlmin! CBlobHandleManagerCommon :: PxvarPreprocessInternal
sqlmin! CValRow :: SetDataX
sqlTsEs! CEsExec :: GeneralEval
sqlmin! CQScanSpoolNew :: PushRow
sqlmin! CQScanSpoolNew ::LoadSpool

(E lembre-se, a pilha de chamadas está de cabeça para baixo, significando o evento mais recente listado primeiro.)

Isso me diz que tem algo a ver com um spool e copiar um valor LOB no espaço de trabalho temporário. Analisei três questões semelhantes (exemplo 1, exemplo 2, exemplo 3); estes não são resolvidos, resolvidos em uma compilação que já temos ou parecem não relacionados.

Leia Também  Criando pacotes SSIS programaticamente usando EzAPI

É intermitente, portanto, execute a consulta novamente um minuto depois poderia trabalho, ou talvez não. Mas dizer à equipe que apenas mexa o polegar e tenta novamente mais tarde não é uma solução.

Eu executei a consulta e, com certeza, quando ela não produz um despejo de pilha, ela possui os seguintes spools:

Uma má combinação - SQLBlog 2Planeje mostrar dois carretéis contra a pilha.

Uma coisa importante a ser observada é que o nvarchar(max) A coluna não precisou ser definida dessa maneira, pois a string mais longa possui 8 caracteres (e se une a uma existente nvarchar(8) coluna).

Podemos reproduzir em uma mesa diferente?

Sem saber se o problema era o heap, a coluna LOB ou a combinação, e ainda confiante de que não era corrupção, criei cópias da tabela, preenchida com os mesmos dados, para tentar isolar o problema:

  • AB_HeapMax – um heap correspondente à estrutura original da tabela.
  • AB_CIXMax – um índice clusterizado (mas coluna problemática ainda nvarchar(max))
  • AB_HeapN8 – um monte, mas a coluna problemática está agora nvarchar(8) (correspondente à coluna de junção).
  • AB_CIXN8 – um índice agrupado (coluna problemática agora nvarchar(8))

Em seguida, executei a mesma consulta dezenas de vezes e, para cada conjunto, substitui esses quatro nomes de tabela pelo heap original. Observações imediatas:

  • AB_HeapMax ainda gerou o despejo de pilha (intermitentemente), cerca de 20% do tempo, sugerindo ainda que a corrupção não é o problema. Curiosamente, quando minha cópia da tabela causou a afirmação, executando a consulta na tabela original imediatamente Além disso causou a afirmação.
  • AB_CIXMax gerou a afirmação com muito menos frequência – cerca de 5% do tempo, sugerindo que o heap sozinho não é o problema, apenas que o torna mais provável. Os carretéis permaneceram.
  • Nenhuma das tabelas sem a coluna max gerou uma única exceção e, nesses casos, os carretéis acabaram.
Leia Também  Aplicar patches do SQL Server ou atualizações cumulativas em grupos de disponibilidade Always On do SQL Server

Agora, isso não pode garantir que o problema nunca ocorra novamente. E, embora você possa sugerir que a solução é tão simples quanto alterar o tipo de dados, acho que o índice em cluster tem benefícios adicionais além de tornar o problema menos frequente. Há ganhos em termos de desempenho e, também, a coluna menor agora pode fazer parte da chave formal (útil para junções e classificação). Aqui está o detalhamento das métricas de uma execução típica:

Uma má combinação - SQLBlog 3Perfis de desempenho das quatro tabelas

Percebemos um pequeno ganho em ambos os casos ao mudar de um heap para um índice em cluster, mas a grande vantagem é quando nos afastamos do heap e use o tipo de dados correto. Eliminamos os carretéis (e as gravações envolvidas), reduzimos pela metade as leituras e obtemos benefícios do paralelismo; combinado, isso reduz o tempo de execução original de 80 segundos para 8 segundos, uma melhoria de 10X.

Sumário

Essa tabela nunca deveria ter sido uma pilha e a coluna não deveria ter sido um LOB. É claro que esses dois fatores por si só não devem levar a afirmações, despejos de pilha e avisos incorretos sobre corrupção, mas esse é um bug que podemos perseguir com a Microsoft, independentemente de fazer alterações na tabela original. Enquanto isso, podemos solucionar o problema (e melhorar o desempenho) alterando a tabela, sem que os usuários tenham que alterar sua consulta e sem esperar uma correção.

*As fotos exibidas neste post pertencem ao post sqlblog.org

cupom com desconto - o melhor site de cupom de desconto cupomcomdesconto.com.br