Estou lutando para minimizar o custo da operação de classificação em um plano de consulta com o aviso Operator used
tempdbto spill data during execution with spill level 2
Encontrei várias postagens relacionadas a dados de derramamento durante a execução com nível de derramamento 1 , mas não nível 2. O nível 1 parece ser causado por estatísticas desatualizadas , e o nível 2? Não encontrei nada relacionado a level 2
.
Achei este artigo muito interessante relacionado a avisos de classificação:
Nunca ignore um aviso de classificação no SQL Server
Meu servidor SQL?
Microsoft SQL Server 2014 (SP2) (KB3171021) - 12.0.5000.0 (X64) 17 de junho de 2016 19:14:09 Copyright (c) Microsoft Corporation Enterprise Edition (64 bits) no Windows NT 6.3 (Build 9600: ) (Hypervisor)
Meu hardware?
executando a consulta abaixo para encontrar o harware:
-- Informações de hardware do SQL Server 2012
SELECT cpu_count AS [Logical CPU Count], hyperthread_ratio AS [Hyperthread Ratio],
cpu_count/hyperthread_ratio AS [Physical CPU Count],
physical_memory_kb/1024 AS [Physical Memory (MB)], affinity_type_desc,
virtual_machine_type_desc, sqlserver_start_time
FROM sys.dm_os_sys_info WITH (NOLOCK) OPTION (RECOMPILE);
memória atualmente alocada
SELECT
(physical_memory_in_use_kb/1024) AS Memory_usedby_Sqlserver_MB,
(locked_page_allocations_kb/1024) AS Locked_pages_used_Sqlserver_MB,
(total_virtual_address_space_kb/1024) AS Total_VAS_in_MB,
process_physical_memory_low,
process_virtual_memory_low
FROM sys.dm_os_process_memory;
quando executo minha consulta com escopo de um ano não recebo nenhum tipo de aviso, conforme a figura abaixo:
Mas quando o executo apenas para escopo de 1 dia, recebo este aviso on the sort operator
:
esta é a consulta:
DECLARE @FromDate SMALLDATETIME = '19-OCT-2016 11:00'
DECLARE @ToDate SMALLDATETIME = '20-OCT-2016 12:00'
SELECT DISTINCT
a.strAccountCode ,
a.strAddressLine6 ,
a.strPostalCode ,
CASE WHEN a.strCountryCode IN ('91','92') THEN 'GB-Int'
ELSE a.strCountryCode
END AS [strCountryCode]
FROM Bocss2.dbo.tblBAccountParticipant AS ap
INNER JOIN Bocss2.dbo.tblBAccountParticipantAddress AS apa ON ap.lngParticipantID = apa.lngParticipantID
AND apa.sintAddressTypeID = 2
INNER JOIN Bocss2.dbo.tblBAccountHolder AS ah ON ap.lngParticipantID = ah.lngParticipantID
INNER JOIN Bocss2.dbo.tblBAddress AS a ON apa.lngAddressID = a.lngAddressID
AND a.blnIsCurrent = 1
INNER JOIN Bocss2.dbo.tblBOrder AS o ON ap.lngParticipantID = o.lngAccountParticipantID
AND o.sdtmOrdCreated >= @FromDate
AND o.sdtmOrdCreated < @ToDate
OPTION(RECOMPILE)
o plano de consulta usando pastetheplan
Dúvidas: 1) no plano de consulta vejo isso:
StatementOptmEarlyAbortReason="GoodEnoughPlanFound" CardinalityEstimationModelVersion="70"
por que 70? Estou usando o sql server 2014
2) como faço para me livrar desse operador de classificação (se possível)?
3) Eu vi a expectativa de vida da página muito baixa, além de adicionar mais memória a este servidor, há alguma outra coisa que eu possa dar uma olhada para ver se posso evitar esse aviso?
Felicidades
Atualização após a resposta de Shanky e Paul White
Eu verifiquei minhas estatísticas de acordo com o script abaixo e elas parecem estar corretas e atualizadas.
estes são todos os índices e tabelas usados nesta consulta.
DBCC SHOW_STATISTICS ('dbo.tblBAddress','IDXF_tblBAddress_lngAddressID__INC')
GO
DBCC SHOW_STATISTICS ('dbo.tblBOrder','IX_tblBOrder_sdtmOrdCreated_INCL')
GO
DBCC SHOW_STATISTICS ('dbo.tblBAccountHolder','PK_tblAccountHolder')
GO
DBCC SHOW_STATISTICS ('dbo.tblBAccountParticipant','PK_tblBAccountParticipants')
GO
DBCC SHOW_STATISTICS ('dbo.tblBAccountParticipantAddress','IDXF_tblBAccountParticipantAddress_lngParticipantID')
GO
isso é o que eu tenho retornado:
Este é um resultado parcial, mas eu revisei todos eles.
Para atualização de estatísticas, atualmente tenho Ola Hallengren
o Index Optimize Job - programado para ser executado uma vez por semana - aos domingos
EXECUTE [dbo].[IndexOptimize]
@Databases = 'USER_DATABASES,-%Archive',
@Indexes = 'ALL_INDEXES' ,
@FragmentationLow = NULL,
@FragmentationMedium = NULL,
@FragmentationHigh = NULL,
@PageCountLevel=1000,
@StatisticsSample =100
,@UpdateStatistics = 'Index',
@OnlyModifiedStatistics = 'Y',
@TimeLimit=10800,
@LogToTable = 'Y'
Embora as estatísticas pareçam estar atualizadas Depois de executar o script a seguir, não recebi mais nenhum aviso no operador de classificação.
UPDATE STATISTICS [Bocss2].[dbo].[tblBOrder] WITH FULLSCAN
--1 hour 04 min 14 sec
UPDATE STATISTICS [Bocss2].[dbo].tblBAddress WITH FULLSCAN
-- 45 min 29 sec
UPDATE STATISTICS [Bocss2].[dbo].tblBAccountHolder WITH FULLSCAN
-- 26 SEC
UPDATE STATISTICS [Bocss2].[dbo].tblBAccountParticipant WITH FULLSCAN
-- 4 min
UPDATE STATISTICS [Bocss2].[dbo].tblBAccountParticipantAddress WITH FULLSCAN
-- 7 min 3 sec
De acordo com este antigo documento do MS , o número no derramamento de Tempdb significa quantas passagens são necessárias sobre os dados para classificá-los. Então Spill 1 significa que tem que passar 1 vez para classificar os dados e 2 significa que tem que passar 2 vezes.
Citando do blog:
Isso ocorre porque o nível de compatibilidade do banco de dados na imagem NÃO é 120 (o que significa nível de compatibilidade do banco de dados 2014), pois não é 120. A consulta será processada usando o modelo antigo de estimativa de cardinalidade (CE), conhecido como
CardinalityEstimationModelVersion="70"
. Tenho certeza de que você está ciente de que, a partir do SQL Server 2014, temos o novo CE.O comando distinto que você está usando está causando a operação de classificação. Os dados que estão sendo classificados não cabem na memória, portanto, são transferidos para o tempdb e, quando isso acontece, um aviso de classificação com um ponto de exclamação amarelo é fornecido no plano de execução. Os avisos de classificação nem sempre são um problema.
Você pode ver no plano de execução que o número estimado de linhas a serem classificadas é 1, mas 16.353 são encontradas no tempo de execução. A quantidade de memória reservada para a classificação é baseada no tamanho esperado (estimado) da entrada e não pode crescer durante a execução (neste caso).
A pequena concessão de memória para a consulta (1632 KB) também é compartilhada entre os operadores que consomem memória em execução simultânea (classificação e junções de loop 'otimizadas' ). Em seu plano, isso significa que 33,33% (544 KB) estão disponíveis para a classificação durante a leitura de linhas (fração de memória de entrada). Isso não é memória suficiente para classificar as 16.353 linhas, portanto, ela é transferida para tempdb . Um derramamento de nível único não é suficiente para concluir a triagem, portanto, é necessário um segundo nível de derramamento (consulte a referência no final para obter mais detalhes sobre os níveis de derramamento).
Classifique as propriedades conforme visualizadas no SQL Sentry Plan Explorer
A atualização das estatísticas provavelmente ajudará com o problema de estimativa de cardinalidade. Você pode estar enfrentando o problema da chave ascendente, principalmente na tabela
tblBOrder
. Uma simples seleção dessa tabela com as datas literais de sua pergunta provavelmente estimará uma linha agora.PLE é uma indicação da quantidade de atividade de E/S, aumentou? Isso acontece com frequência ou apenas quando você executa determinada consulta ou isso ocorreu apenas hoje. Evite a reação instintiva, primeiro precisamos ter certeza de que você realmente está enfrentando pressão de memória ou alguma consulta desonesta que está gerando muita E/S está causando isso. De qualquer forma, você já possui 97 G de memória atribuída ao SQL Server.
Para obter mais informações sobre os níveis de derramamento e o problema da chave ascendente, consulte: