Contenção de Tempdb, como resolver?
Olá pessoal, tudo certo?
Como no Brasil o ano só começa depois do carnaval, estamos de volta com as baterias recarregadas para mais um ano de muitos compartilhamento de informação e conhecimento.
Neste primeiro post do ano quero compartilhar com vocês a experiência que tive logo na primeira semana do ano. A principal instância da empresa que trabalho começou a ter sérios problemas de performance, ao realizar uma analise percebi que estava tendo uma quantidade muito elevada de PAGELATCH_UP, que significa que estava tendo uma contenção no Tempdb, especificamente uma contenção na PFS (Page Free Space), que é responsável por registrar o status de alocação de cada pagina, ou seja, responsável por mapear a quantidade de espaço livre tem cada pagina.
O que me chamou a atenção para o problema de contenção foi o resultado da execução da sp_whoisactive, mostrando vários wait_info de PAGELATCH_UP:tempdb:1(PFS).
Ao verificar o resultado acima executei um script que utilizo, desenvolvido pelo Paul Randal (link).
WITH [Waits] AS (SELECT [wait_type], [wait_time_ms] / 1000.0 AS [WaitS], ([wait_time_ms] - [signal_wait_time_ms]) / 1000.0 AS [ResourceS], [signal_wait_time_ms] / 1000.0 AS [SignalS], [waiting_tasks_count] AS [WaitCount], 100.0 * [wait_time_ms] / SUM ([wait_time_ms]) OVER() AS [Percentage], ROW_NUMBER() OVER(ORDER BY [wait_time_ms] DESC) AS [RowNum] FROM sys.dm_os_wait_stats WHERE [wait_type] NOT IN ( N'BROKER_EVENTHANDLER', N'BROKER_RECEIVE_WAITFOR', N'BROKER_TASK_STOP', N'BROKER_TO_FLUSH', N'BROKER_TRANSMITTER', N'CHECKPOINT_QUEUE', N'CHKPT', N'CLR_AUTO_EVENT', N'CLR_MANUAL_EVENT', N'CLR_SEMAPHORE', N'DBMIRROR_DBM_EVENT', N'DBMIRROR_EVENTS_QUEUE', N'DBMIRROR_WORKER_QUEUE', N'DBMIRRORING_CMD', N'DIRTY_PAGE_POLL', N'DISPATCHER_QUEUE_SEMAPHORE', N'EXECSYNC', N'FSAGENT', N'FT_IFTS_SCHEDULER_IDLE_WAIT', N'FT_IFTSHC_MUTEX', N'HADR_CLUSAPI_CALL', N'HADR_FILESTREAM_IOMGR_IOCOMPLETION', N'HADR_LOGCAPTURE_WAIT', N'HADR_NOTIFICATION_DEQUEUE', N'HADR_TIMER_TASK', N'HADR_WORK_QUEUE', N'KSOURCE_WAKEUP', N'LAZYWRITER_SLEEP', N'LOGMGR_QUEUE', N'ONDEMAND_TASK_QUEUE', N'PWAIT_ALL_COMPONENTS_INITIALIZED', N'QDS_PERSIST_TASK_MAIN_LOOP_SLEEP', N'QDS_SHUTDOWN_QUEUE', N'QDS_CLEANUP_STALE_QUERIES_TASK_MAIN_LOOP_SLEEP', N'REQUEST_FOR_DEADLOCK_SEARCH', N'RESOURCE_QUEUE', N'SERVER_IDLE_CHECK', N'SLEEP_BPOOL_FLUSH', N'SLEEP_DBSTARTUP', N'SLEEP_DCOMSTARTUP', N'SLEEP_MASTERDBREADY', N'SLEEP_MASTERMDREADY', N'SLEEP_MASTERUPGRADED', N'SLEEP_MSDBSTARTUP', N'SLEEP_SYSTEMTASK', N'SLEEP_TASK', N'SLEEP_TEMPDBSTARTUP', N'SNI_HTTP_ACCEPT', N'SP_SERVER_DIAGNOSTICS_SLEEP', N'SQLTRACE_BUFFER_FLUSH', N'SQLTRACE_INCREMENTAL_FLUSH_SLEEP', N'SQLTRACE_WAIT_ENTRIES', N'WAIT_FOR_RESULTS', N'WAITFOR', N'WAITFOR_TASKSHUTDOWN', N'WAIT_XTP_HOST_WAIT', N'WAIT_XTP_OFFLINE_CKPT_NEW_LOG', N'WAIT_XTP_CKPT_CLOSE', N'XE_DISPATCHER_JOIN', N'XE_DISPATCHER_WAIT', N'XE_TIMER_EVENT') AND [waiting_tasks_count] > 0 ) SELECT MAX ([W1].[wait_type]) AS [WaitType], CAST (MAX ([W1].[WaitS]) AS DECIMAL (16,2)) AS [Wait_S], CAST (MAX ([W1].[ResourceS]) AS DECIMAL (16,2)) AS [Resource_S], CAST (MAX ([W1].[SignalS]) AS DECIMAL (16,2)) AS [Signal_S], MAX ([W1].[WaitCount]) AS [WaitCount], CAST (MAX ([W1].[Percentage]) AS DECIMAL (5,2)) AS [Percentage], CAST ((MAX ([W1].[WaitS]) / MAX ([W1].[WaitCount])) AS DECIMAL (16,4)) AS [AvgWait_S], CAST ((MAX ([W1].[ResourceS]) / MAX ([W1].[WaitCount])) AS DECIMAL (16,4)) AS [AvgRes_S], CAST ((MAX ([W1].[SignalS]) / MAX ([W1].[WaitCount])) AS DECIMAL (16,4)) AS [AvgSig_S] FROM [Waits] AS [W1] INNER JOIN [Waits] AS [W2] ON [W2].[RowNum] <= [W1].[RowNum] GROUP BY [W1].[RowNum] HAVING SUM ([W2].[Percentage]) - MAX ([W1].[Percentage]) < 95; -- percentage threshold GO
Que comprovou que o Wait Type que estava gerando o gargalo na minha instância era o PAGELATCH_UP.
Para identificar que estava tendo um problema de contenção na Tempdb executei um outro script no momento da lentidão, que identifica os wait_type e diz qual o tipo de recurso que ele esta gerando gargalo.
Select session_id, wait_type, wait_duration_ms, blocking_session_id, resource_description, ResourceType = Case When Cast(Right(resource_description, Len(resource_description) - Charindex(':', resource_description, 3)) As Int) - 1 % 8088 = 0 Then 'Is PFS Page' When Cast(Right(resource_description, Len(resource_description) - Charindex(':', resource_description, 3)) As Int) - 2 % 511232 = 0 Then 'Is GAM Page' When Cast(Right(resource_description, Len(resource_description) - Charindex(':', resource_description, 3)) As Int) - 3 % 511232 = 0 Then 'Is SGAM Page' Else 'Is Not PFS, GAM, or SGAM page' End From sys.dm_os_waiting_tasks Where wait_type Like 'PAGE%LATCH_%' And resource_description Like '2:%'
O resultado acima mostra que o database id 2, que é o Tempdb, o arquivo de dados 1, e a pagina 1 que é a PFS estava sofrendo com o PAGELATCH_UP e estava gerando a lentidão da instância.
Uma das possíveis solução e até mesmo evitar que este problema ocorra, é adicionar mais arquivos de dados ao Tempdb, para que o SQL Server possa distribuir as requisições.
Quando criamos vários arquivos de dados para o Tempdb o SQL Server passa a utilizar um algoritmo de preenchimento proporcional para determinar qual arquivo ele vai utilizar para cada requisição. Os arquivos devem ser configurados no mesmo tamanho e ter o mesmo autogrowth, eles podem estar no mesmo disco, mas recomendo se possível colocar em discos separados por questão de IO no disco.
Quanto a quantidade de arquivos de dados em linha geral a Microsoft recomenda 1 arquivo de dados para 1CPU lógica, se o numero de processadores lógicos for maior que 8, é recomendado usar 8 arquivos, porem se a contenção persistir aumente o numero de arquivos de dados em múltiplos de 4 até o numero de processadores lógicos (https://support.microsoft.com/pt-br/kb/2154845).
Para adicionar adicionar os arquivos de dados você pode fazer via GUI, ou por script, nesta demo como o meu servidor está configurado com 4 processadores estou adicionando mais 3 arquivos de dados totalizando 4 arquivos de dados para 4 processadores.
USE [master] GO ALTER DATABASE [tempdb] ADD FILE ( NAME = N'Tempdb_1_Data', FILENAME = N'H:\tempdb_1_data.ndf' , SIZE = 5120000KB , MAXSIZE = 15360000KB , FILEGROWTH = 1048576KB ) GO ALTER DATABASE [tempdb] ADD FILE ( NAME = N'Tempdb_2_Data', FILENAME = N'I:\tempdb_2_data.ndf' , SIZE = 5120000KB , MAXSIZE = 15360000KB , FILEGROWTH = 1048576KB ) GO ALTER DATABASE [tempdb] ADD FILE ( NAME = N'Tempdb_3_Data', FILENAME = N'J:\tempdb_3_data.ndf' , SIZE = 5120000KB , MAXSIZE = 15360000KB , FILEGROWTH = 1049600KB ) GO ALTER DATABASE [tempdb] MODIFY FILE ( NAME = N'tempdev', SIZE = 5120000KB , MAXSIZE = 15360000KB , FILEGROWTH = 1048576KB ) GO
Ou via interface gráfica.
Botão direito no tempdb
Propriedades – Files
Adiciona os arquivos, os arquivos pode ser adicionado todos no mesmo disco, mas por boa pratica é recomendado que coloque os arquivos em discos separados.
Após a adição dos novos arquivos é necessário reiniciar a instância, após reiniciar a instância o SQL Server vai começar a distribuir as requisições entre os arquivos,
O problema de performance por contenção do Tempdb foi solucionado na instância, venho acompanhando os contadores para verificar se tem tido muitas ocorrências de PAGELATCH_UP, o que não vem acontecendo.
Bom por hoje é isso, e que o ano comece de maneira produtiva para todos e que Deus nos ajude.
Abraços,
Tiago Neves
Boa tarde
Como e a execução da sp_whoisactive?
Boa tarde,
Cara a sp_whoisactive foi desenvolvida pelo Adam Machanic, http://sqlblog.com/blogs/adam_machanic/archive/2012/03/22/released-who-is-active-v11-11.aspx
Para executa-la você tem que ser sysadmin ou ter acesso na View server state, ela captura tudo que esta sendo executado no momento na sua instância e retorna informações importante, sobre lock, wait, status…
Se tiver mais alguma duvida é só postar.
Abraços
Tiago Neves
Show, Tiago!
Muito bom o artigo. Simples e objetivo.
Show de Bola Tiago!
Tiago,
Sofremos com esse problema e conseguimos resolver conforme a solução proposta aqui.
Porém após resolver os problemas de PAGELATCH_UP adicionando novos data files conforme recomendado começamos a utilizar mais CPU, como já estávamos no limite a aplicação ficou offline novamente.
Você tem a resposta se realmente aumentar a quantidade de data files no temp DB requer mais CPU ou foi só uma coincidência?
Postei a mesma questão aqui:
https://stackoverflow.com/questions/48894536/would-increasing-datafiles-to-the-tempdb-affect-the-cpu-usage-load
Parabéns pelo artigo e obrigado foi muito útil.
Denis bom dia,
Primeiramente obrigado pela visita e que bom que o post foi útil.
respondendo a sua duvida, provavelmente além da contenção no tempdb você também estava sofrendo pressão de CPU.
Você tem histórico de como estava o CPU antes de adicionar os novos datafiles ?
Abraços,
Tiago Neves
Antes do problema de Page PAGELATCH_UP começar era “normal” o sistema atingir 80% de CPU no dia que o problema do PAGELATCH_UP aconteceu o CPU sofreu a pressão e atingiu 100%, ao resolver o problema de PAGELATCH_UP o CPU continuou atingindo 100%.
Esse é o cenário que tenho.
Obrigado,
Então você ja estava tendo problema de pressão de CPU não necessariamente problema de contenção da tempdb. Você chegou de conferir se estava tendo contenção na PFS?
O que você pode fazer agora é identificar as queries que mais estão consumindo CPU e trata-las, umas das coisas que mais afeta CPU é conversão implícita.
Com a query abaixo você consegue a informação das suas queries que mais consome CPU, ai vai tratando.
SELECT TOP 10 SUBSTRING(qt.TEXT, (qs.statement_start_offset/2)+1,
((CASE qs.statement_end_offset
WHEN -1 THEN DATALENGTH(qt.TEXT)
ELSE qs.statement_end_offset
END – qs.statement_start_offset)/2)+1),
qs.execution_count,
qs.total_logical_reads, qs.last_logical_reads,
qs.total_logical_writes, qs.last_logical_writes,
qs.total_worker_time,
qs.last_worker_time,
qs.total_elapsed_time/1000000 total_elapsed_time_in_S,
qs.last_elapsed_time/1000000 last_elapsed_time_in_S,
qs.last_execution_time,
qp.query_plan
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) qt
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) qp
ORDER BY qs.total_logical_reads DESC
Espero ter esclarecido suas duvidas.
Opa amigo… O link do comando do sp_whoisactive esta fora do ar…. tem como me mandar a consulta do WHOISACTIVE ??? ou a página onde posso pegar?
email: [email protected]
Abraço!
Olá Carlos,
O link foi alterado, o link correto é o http://whoisactive.com/
Muito obrigado pela visita.
Muito bom o post.. show, gostei mesmo