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).

contencao_tempdb_result_spwhoisactive

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.

contencao_tempdb_result_waittypes

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:%'

contencao_tempdb_PFS_PAGE

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

contencao_tempdb_Add_Tempdb_File

Propriedades – Files

contencao_tempdb_Add_Tempdb_File_Properties

Adiciona os arquivos, os arquivos pode ser adicionado todos no mesmo disco, mas por boa pratica é recomendado que coloque os arquivos em discos separados.

contencao_tempdb_Add_Tempdb_File_add_files

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,

contencao_tempdb_affeter_add_archive

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

15 Comments

Deixe uma resposta