Qual é um método determinístico para avaliar um tamanho sensível do buffer pool?


29

Estou tentando encontrar uma maneira sensata de entender se a max server memory (mb)configuração é apropriada (deve ser mais baixa ou mais alta ou permanecer do jeito que é). Estou ciente de que max server memory (mb)sempre deve ser baixo o suficiente para deixar espaço para o próprio sistema operacional, etc.

O ambiente que estou vendo possui várias centenas de servidores; Preciso de uma fórmula confiável que eu possa usar para determinar se o tamanho atual do pool de buffers é apropriado, pois a RAM é custada por GB alocado para cada servidor. Todo o ambiente é virtualizado e a RAM "física" alocada para uma VM pode ser facilmente alterada para cima ou para baixo.

Eu tenho uma instância específica do SQL Server que estou vendo agora com um PLE de 1.100.052 segundos, o que equivale a 12,7 dias (a quantidade de tempo em que o servidor esteve ativo). O servidor possui uma configuração de memória máxima do servidor de 2560 MB (2,5 GB), dos quais apenas 1380 MB (1,3 GB) são realmente confirmados.

Eu li vários itens, incluindo um de Jonathan Keheyias ( post ) e outro de Paul Randal ( post ), e vários outros. Jonathan defende que o monitoramento de um PLE abaixo de 300 por 4 GB de buffer pool seja muito baixo. Para a instância do SQL Server acima, 300 * (2.5 / 4) = 187resulta em um PLE realmente muito baixo, abaixo de 300. Essa instância possui 290 GB de dados do SQL Server (não incluindo arquivos de log) e é usada apenas para testes de integração. Supondo que os últimos 12 dias sejam representativos do uso típico desse servidor, eu diria que a max server memory (mb)configuração pode ser reduzida.

No outro extremo da escala, tenho outro servidor de teste de integração com um PLE de 294, que possui uma max server memory (mb)configuração de apenas 1 GB. Este servidor possui apenas 224 MB de dados do SQL Server, sem incluir logs, e está executando alguns bancos de dados do BizFlow. Este servidor pode se beneficiar de uma max server memory (mb)configuração mais alta .

Estou pensando que um bom ponto de partida para destinos aos quais pode ser atribuída muita memória pode incluir:

SELECT 
    RamMB = physical_memory_in_bytes / 1048576
    , BufferPoolCommittedMB = bpool_committed * 8192E0 / 1048576
    , BufferPoolCommitTargetMB = bpool_commit_target * 8192E0 / 1048576
    , PercentOfDesiredSizeMB = CONVERT(INT,(CONVERT(DECIMAL(18,2),bpool_committed) 
                            / bpool_commit_target) * 100)
FROM sys.dm_os_sys_info;

Se BufferPoolCommitTargetMB / BufferPoolCommittedMBfor maior que 1, o servidor não está usando o buffer pool inteiro. Se a máquina em questão também tiver um PLE maior que "x", poderá ser um bom candidato para uma diminuição de max server memory (mb).

Como o Buffer Manager:Lazy writes/seccontador de desempenho rastreia o número de vezes que o SQLOS gravou páginas no disco entre os pontos de verificação devido à pressão da memória, isso pode ser outra coisa boa de se olhar.

DECLARE @WaitTime DATETIME;
SET @WaitTime = '00:00:15';
DECLARE @NumSeconds INT;
SET @NumSeconds = DATEDIFF(SECOND, 0, @WaitTime);
DECLARE @LazyWrites1 BIGINT;
DECLARE @LazyWrites2 BIGINT;

SELECT @LazyWrites1 = cntr_value 
FROM sys.dm_os_performance_counters dopc
WHERE (
        dopc.counter_name LIKE 'Lazy writes/sec%' COLLATE SQL_Latin1_General_CP1_CI_AS
    )
    AND dopc.object_name = 'MSSQL$' + CONVERT(VARCHAR(255),
               SERVERPROPERTY('InstanceName')) + ':Buffer Manager';

WAITFOR DELAY @WaitTime;

SELECT @LazyWrites2 = cntr_value 
FROM sys.dm_os_performance_counters dopc
WHERE (
        dopc.counter_name LIKE 'Lazy writes/sec%' COLLATE SQL_Latin1_General_CP1_CI_AS
    )
    AND dopc.object_name = 'MSSQL$' + CONVERT(VARCHAR(255),
               SERVERPROPERTY('InstanceName')) + ':Buffer Manager';

SELECT LazyWritesPerSecond = (@LazyWrites2 - @LazyWrites1) / @NumSeconds;

O código acima supõe que o servidor esteja sob carga durante os 15 segundos necessários para executar, caso contrário, ele informará 0; o que pode ser falso-negativo enganoso.

Devo também considerar as PAGELATCHIO_*estatísticas de espera ou algum outro tipo de espera como um indicador da pressão da memória ou da falta dela?

Minha pergunta é: como posso determinar com segurança um valor-alvo "bom" para o PLE e max server memory (mb)?

Respostas:


11

Como você já sabe que não existe uma fórmula geral para calcular a memória máxima do servidor, você pode fazer cálculos rápidos e chegar a um valor, mas ainda assim precisaria da ajuda dos contadores Perfmon para monitorar o uso da memória e alterar em conformidade. Sei abaixo a fórmula geral e também a uso. Eu aprendi essa fórmula com este link

Para SQL Server 2005 a 2008 R2

Observe que do SQL Server 2005 a 2008 R2 a memória máxima do servidor controla apenas o buffer pool. Portanto, a configuração máxima da memória do servidor é um pouco tediosa aqui e envolve poucos cálculos

  1. Deixe a memória 2 G imediatamente para o sistema operacional Windows.

  2. Claro que o sistema teria antivírus em execução. Deixe 1.5G para o antivírus. Observe que o Mcafee e o SQL Server não andam de mãos dadas, portanto, deixe o suficiente para isso. Você também pode verificar o contador perfmon Perfmon Process-> Private bytes and Working Setpara monitorar o uso de memória pelo AV e outros aplicativos pequenos em execução no SQL Server

insira a descrição da imagem aqui

  1. Considere os requisitos de memória dos drivers / firmwares. Você deve derivá-lo com base nos requisitos de memória dos drivers instalados no sistema. A ferramenta RAMMAP pode ajudar

  2. Considere os requisitos de memória do NonbPool (também conhecido como MTL ou MTR) do SQL Server.

    select  sum(multi_pages_kb)/1024 as multi_pages_mb from  sys.dm_os_memory_clerks

    + Máximo de threads de trabalho * 2 MB

    + Memória para alocações diretas do Windows de aproximadamente 0 a 300 MB na maioria dos casos, mas pode ser necessário aumentá-la se houver muitos componentes de 3 partes carregados no processo do SQL Server (incluindo dlls de servidor vinculado, dlls de backup de terceiros etc.)

    + Se você estiver usando o CLR, adicione extensivamente memória adicional para o CLR.

  3. Considere o requisito de memória dos trabalhos (incluindo agentes de replicação, envio de logs etc.) e pacotes que serão executados no servidor. Pode variar de MB a GB, de acordo com o número de tarefas em execução. Para servidor de tamanho médio, você pode ter 250 MB

  4. Verifique se há espaço livre suficiente para o sistema operacional.

    Aproximadamente (100 MB para cada GB até 4G) + (50 MB para cada GB adicional até 12 GB) + (25 MB para cada GB adicional até o tamanho da RAM)

  5. Outros requisitos de memória.

    Se você tiver algum outro requisito de memória específico para o seu ambiente.

    Memória máxima do servidor = Memória física total - (1 + 2 + 3 + 4 + 5 + 6 + 7)

    Não incluí a configuração de memória para SSIS.SSRS, SSAS. Você também precisará subtrair a memória exigida por esses serviços da memória total do servidor físico.

    Depois de configurar acima, você precisa monitorar os seguintes contadores

  • SQLServer: Buffer Manager - PLE (Expectativa de vida útil da página):

  • SQLServer: Buffer Manager - CheckpointPages / s:

  • SQLServer: Memory Manager - Concessões de memória pendentes:

  • SQLServer: memory Manager - Memória do servidor de destino:

  • SQLServer: memory Manager - Memória total do servidor

Para o SQL Server 2012/2014.

From SQL Server 2012 onwardsconfigurar a memória máxima do servidor tornou-se fácil. Porque agora a memória máxima do servidor quase responde por todo o consumo de memória. A memória máxima do servidor controla a alocação de memória do SQL Server, incluindo o pool de buffers, a memória de compilação, todos os caches, as concessões de memória qe, a memória do gerenciador de bloqueios e a memória CLR (basicamente qualquer "funcionário", conforme encontrado em dm_os_memory_clerks). A memória para pilhas de encadeamentos, pilhas, provedores de servidor vinculados que não sejam o SQL Server ou qualquer memória alocada por uma DLL "não SQL Server" não é controlada pela memória máxima do servidor.

Você pode alocar 75-80% para o SQL Server e, em seguida, usar contadores perfmon para monitorar o uso da memória. No SQL Server 2012, alguns contadores de desempenho foram preteridos. O contador do gerenciador de buffer está obsoleto. Você deve usar o contador do gerenciador de memória

  • SQL Server: Gerenciador de memória - memória do servidor de destino (KB)

  • SQL Server: Gerenciador de memória - memória total do servidor (KB)

  • SQL Server: Gerenciador de Memória - Memória Livre (KB)

  • SQL Server: Gerenciador de memória - memória cache do banco de dados (KB)

No valor do PLE, usei a fórmula de Joanthan e, felizmente, funcionou para mim.


6

O desafio aqui é que os números não levam em consideração a experiência do usuário final.

Ótimo exemplo: tenho um servidor de banco de dados usado para rastrear todos os sites visitados pelos funcionários da empresa. Não me importo se ele não consegue acompanhar as inserções durante os picos de carga, porque o aplicativo front-end agrupa as inserções periodicamente e as inserções lentas não causam problemas para os usuários. Os usuários ainda podem navegar na web sem serem pressionados por inserções lentas.

No momento do SELECT, o departamento de RH apenas envia relatórios quando solicitados pelo histórico de navegação suspeito para um determinado funcionário, mas eles não se importam com o tempo que os relatórios levam - eles apenas abrem o relatório e partem para fazer outras coisas.

O desempenho precisa começar com uma pergunta: os usuários estão satisfeitos com o desempenho? Nesse caso, deixe o sistema onde está.


Mesmo se você estiver usando mais memória do que precisa?
James Anderson

2
James - de um modo geral, não quero fazer alterações que façam com que os usuários se queixem. Se você quiser fazer isso, poderá reduzir gradualmente a quantidade de memória de cada servidor até que os usuários comecem a reclamar, mas quando já estou sobrecarregado, geralmente não tenho tempo para tomar essas medidas. Tenho que me concentrar nas tarefas que farão felizes os usuários infelizes - em vez de tentar fazê-los felizes. ;-)
Brent Ozar

2
Bons pontos, Brent. Me pediram para ver se alguns servidores estão com excesso de provisionamento porque pagamos por memória por GB por ano. Muitas das instâncias que estou vendo têm o que considero uma quantidade muito pequena de RAM max server memory (mb)e, como tal, reluto em reduzi-las. No entanto, algumas outras instâncias têm 1.000.000 de PLE e, como tal, são potenciais candidatos bastante óbvios para uma queda na RAM. Claramente, baixando RAM vai causar um aumento na IOPS, e eu não tenho certeza do que o custo do que será.
Max Vernon

1
Além disso, observar o PLE em comparação com o max server memorycenário é uma coisa de galinha e ovo; quanto menor a max server memoryconfiguração, menor seria o PLE mínimo "aceitável", para que eu pudesse ficar preso em uma espiral cada vez menor. Tenho certeza de que, como você mencionou, o desempenho do usuário será afetado em algum momento .
Max Vernon

Os contadores PLE são aqueles que você deve evitar procurar a partir de 2012 ou quando tiver um sistema NUMA em que cada nó se comporta como um pequeno alocador de memória. Se você quiser você deve procurar PLE para cada nó NUMA não concluir que você pode obter valor incorreto
shanky

3

O atual T-SQL que estou usando para avaliar o PLE vs max server memoryé:

/*
    Purpose:            Returns a resultset describing various server level stats including PLE
                        Max and Min Server Memory, etc.
    By:                 Max Vernon
    Date:               2014-12-01
*/
SET NOCOUNT ON;

/*
    wait stats for PAGELATCH_IO
*/
DECLARE @Debug BIT;
SET @Debug = 0;
DECLARE @HTMLOutput BIT;
SET @HTMLOutput = 1;
DECLARE @WaitTime DATETIME;
SET @WaitTime = '00:00:15';
DECLARE @NumSeconds INT;
SET @NumSeconds = DATEDIFF(SECOND, 0, @WaitTime);
DECLARE @InstanceName NVARCHAR(255);
SET @InstanceName = CONVERT(NVARCHAR(255), SERVERPROPERTY('InstanceName'));
DECLARE @Version NVARCHAR(255);
DECLARE @VersionINT INT;
SET @Version = CONVERT(NVARCHAR(255),SERVERPROPERTY('ProductVersion'));
SET @VersionINT = CONVERT(INT, SUBSTRING(@Version,1 ,CHARINDEX('.',@Version)-1));
DECLARE @cmd NVARCHAR(MAX);
SET @cmd = '';
DECLARE @TaskCount INT;
DECLARE @TasksPerSecondAvg INT;
DECLARE @AvgWaitTimeInMSPerTask DECIMAL(10,2);
DECLARE @AvgWaitTimeInMSPerSecond DECIMAL(10,2);
DECLARE @TotalWaitTimeInMSOverall DECIMAL(10,2);
DECLARE @LazyWrites1 BIGINT;
DECLARE @LazyWrites2 BIGINT;
DECLARE @FreeListStallsSec1 BIGINT;
DECLARE @FreeListStallsSec2 BIGINT;
DECLARE @BatchReq1 BIGINT;
DECLARE @BatchReq2 BIGINT;
DECLARE @ws TABLE
(
    RunNum INT
    , wait_type SYSNAME
    , waiting_tasks_count BIGINT
    , wait_time_ms BIGINT
    , max_wait_time_ms BIGINT
    , signal_wait_time_ms BIGINT
);
INSERT INTO @ws
SELECT 1, dows.*
FROM sys.dm_os_wait_stats dows
WHERE dows.wait_type LIKE 'PAGEIOLATCH_%'
ORDER BY dows.waiting_tasks_count DESC;

SELECT @LazyWrites1 = cntr_value 
FROM sys.dm_os_performance_counters dopc
WHERE (
        dopc.counter_name LIKE N'Lazy writes/sec%' COLLATE SQL_Latin1_General_CP1_CI_AS
    )
    AND dopc.object_name = N'MSSQL$' + @InstanceName + N':Buffer Manager';

SELECT @FreeListStallsSec1 = cntr_value 
FROM sys.dm_os_performance_counters dopc
WHERE (
        dopc.counter_name LIKE N'Free list stalls/sec%' COLLATE SQL_Latin1_General_CP1_CI_AS
    )
    AND dopc.object_name = N'MSSQL$' + @InstanceName + N':Buffer Manager';

SELECT @BatchReq1 = cntr_value
FROM sys.dm_os_performance_counters dopc
WHERE dopc.counter_name LIKE N'Batch Requests/sec%' COLLATE SQL_Latin1_General_CP1_CI_AS
    AND dopc.object_name = N'MSSQL$' + @InstanceName + N':SQL Statistics';

WAITFOR DELAY @WaitTime;

INSERT INTO @ws
SELECT 2, dows.*
FROM sys.dm_os_wait_stats dows
WHERE dows.wait_type LIKE N'PAGEIOLATCH_%'
ORDER BY dows.waiting_tasks_count DESC;

SELECT @LazyWrites2 = cntr_value 
FROM sys.dm_os_performance_counters dopc
WHERE (
        dopc.counter_name LIKE N'Lazy writes/sec%' COLLATE SQL_Latin1_General_CP1_CI_AS
    )
    AND dopc.object_name = N'MSSQL$' + @InstanceName + N':Buffer Manager';

SELECT @FreeListStallsSec2 = cntr_value 
FROM sys.dm_os_performance_counters dopc
WHERE (
        dopc.counter_name LIKE N'Free list stalls/sec%' COLLATE SQL_Latin1_General_CP1_CI_AS
    )
    AND dopc.object_name = N'MSSQL$' + @InstanceName + N':Buffer Manager';

SELECT @TaskCount = SUM(w2.waiting_tasks_count - w1.waiting_tasks_count)
    , @TasksPerSecondAvg = CONVERT(DECIMAL(10,2), (SUM(w2.waiting_tasks_count) - SUM(w1.waiting_tasks_count))) / @NumSeconds
    , @AvgWaitTimeInMSPerTask = CONVERT(DECIMAL(10,2),(SUM(w2.wait_time_ms) - SUM(w1.wait_time_ms))) / CONVERT(DECIMAL(10,2),(SUM(w2.waiting_tasks_count) - SUM(w1.waiting_tasks_count)))
    , @AvgWaitTimeInMSPerSecond = (CONVERT(DECIMAL(10,2), (SUM(w2.waiting_tasks_count) - SUM(w1.waiting_tasks_count))) / @NumSeconds) * (CONVERT(DECIMAL(10,2),(SUM(w2.wait_time_ms) - SUM(w1.wait_time_ms))) / CONVERT(DECIMAL(10,2),(SUM(w2.waiting_tasks_count) - SUM(w1.waiting_tasks_count))))
    , @TotalWaitTimeInMSOverall = SUM(w2.wait_time_ms) - SUM(w1.wait_time_ms)
FROM (SELECT * FROM @ws ws1 WHERE ws1.RunNum = 1) w1
    INNER JOIN (SELECT * FROM @ws ws2 WHERE ws2.RunNum = 2) w2 ON w1.wait_type = w2.wait_type
WHERE (w2.waiting_tasks_count - w1.waiting_tasks_count) > 0;

SELECT @BatchReq2 = cntr_value
FROM sys.dm_os_performance_counters dopc
WHERE dopc.counter_name LIKE N'Batch Requests/sec%' COLLATE SQL_Latin1_General_CP1_CI_AS
    AND dopc.object_name = N'MSSQL$' + @InstanceName + N':SQL Statistics';

/*
    configured values for max server memory and min server memory, etc
*/
DECLARE @MaxServerMemory BIGINT;
DECLARE @MaxServerMemoryPages BIGINT;
DECLARE @MinServerMemory BIGINT;
DECLARE @MinPLE BIGINT;
DECLARE @RamMB BIGINT;
DECLARE @BufferPoolCommittedMB BIGINT;
DECLARE @BufferPoolCommitTargetMB BIGINT;
DECLARE @PercentOfDesiredSizeMB INT;
DECLARE @TargetPageLifeExpectancyPer4GB BIGINT;
SET @TargetPageLifeExpectancyPer4GB = 60 * 120; /* 120 minutes */
/*DECLARE @VMType VARCHAR(255);*/
DECLARE @PLESeconds BIGINT;

SELECT @MaxServerMemory = CONVERT(BIGINT,c.value)
FROM sys.configurations c
WHERE c.name = N'max server memory (mb)'

SET @MaxServerMemoryPages = @MaxServerMemory / 128; /* 8KB pages */

SELECT @MinServerMemory = CONVERT(BIGINT,c.value)
FROM sys.configurations c
WHERE c.name = N'min server memory (mb)'

SET @MinPLE = @MaxServerMemory / 4096E0 * @TargetPageLifeExpectancyPer4GB;

IF @VersionINT < 11
BEGIN
    SET @cmd = 'SELECT 
    @RamMB = dosi.physical_memory_in_bytes / 1048576
    , @BufferPoolCommittedMB = dosi.bpool_committed * 8192E0 / 1048576
    , @BufferPoolCommitTargetMB = dosi.bpool_commit_target * 8192E0 / 1048576
    , @PercentOfDesiredSizeMB = CONVERT(INT,(CONVERT(DECIMAL(18,2),dosi.bpool_committed) / dosi.bpool_commit_target) * 100)
FROM sys.dm_os_sys_info dosi;
';
END
ELSE 
BEGIN 
SET @cmd = 'SELECT 
    @RamMB = dosi.physical_memory_kb / 1024
    , @BufferPoolCommittedMB = dosi.committed_kb / 1024
    , @BufferPoolCommitTargetMB = dosi.committed_target_kb / 1024
    , @PercentOfDesiredSizeMB = CONVERT(INT,(CONVERT(DECIMAL(18,2),dosi.committed_kb) / dosi.committed_target_kb) * 100)
FROM sys.dm_os_sys_info dosi;';
END
EXEC sp_executesql @cmd
    , N'@RamMB BIGINT OUTPUT, @BufferPoolCommittedMB BIGINT OUTPUT, @BufferPoolCommitTargetMB BIGINT OUTPUT, @PercentOfDesiredSizeMB INT OUTPUT' 
    , @RamMB = @RamMB OUT
    , @BufferPoolCommittedMB = @BufferPoolCommittedMB OUT
    , @BufferPoolCommitTargetMB = @BufferPoolCommitTargetMB OUT
    , @PercentOfDesiredSizeMB = @PercentOfDesiredSizeMB OUT;

/*
    Page Life Expectancy for all memory nodes
*/
SELECT @PLESeconds = CONVERT(BIGINT, cntr_value) 
FROM sys.dm_os_performance_counters dopc
WHERE dopc.counter_name LIKE N'Page Life Expectancy%' COLLATE SQL_Latin1_General_CP1_CI_AS
    AND dopc.object_name = N'MSSQL$' + @InstanceName + N':Buffer Manager';

/*
    Total data in all user-databases.
*/
DECLARE @TotalDBSpaceUsed TABLE
(
    TotalSpaceUsedInMB BIGINT
);
DECLARE @SpaceUsedInMB BIGINT;
SET @cmd = '';
SELECT @cmd = @cmd + CASE WHEN @cmd = '' THEN '' ELSE '
UNION ALL
' END + 
'
SELECT DatabaseName = ''' + d.name + ''' 
    , AllocType = au.type_desc
    , TotalPagesInMB = SUM(au.total_pages) * 8192E0 / 1048576
FROM ' + QUOTENAME(d.name) + '.sys.allocation_units au
WHERE au.type > 0
GROUP BY au.type_desc
'
FROM master.sys.databases d
WHERE d.database_id > 4;
SET @cmd = 'SELECT SUM(TotalPagesInMB)
FROM (
' + @cmd + '
) t;'; 
INSERT INTO @TotalDBSpaceUsed (TotalSpaceUsedInMB)
EXEC sp_executesql @cmd;
SELECT @SpaceUsedInMB = TDSU.TotalSpaceUsedInMB
FROM @TotalDBSpaceUsed TDSU;

IF @Debug = 1
BEGIN
    SELECT ServerName = @@SERVERNAME
        , InstanceName = @InstanceName
        , DatabaseSpaceUsedMB = @SpaceUsedInMB
        , PLEinSeconds = @PLESeconds
        , MinAcceptablePLE = @MinPLE
        , MinServerMemoryMB = @MinServerMemory
        , MaxServerMemoryMB = @MaxServerMemory
        , TotalServerRAMinMB = @RamMB
        , BufferPoolCommittedMB = @BufferPoolCommittedMB
        , BufferPoolCommitTargetMB = @BufferPoolCommitTargetMB
        , PercentBufferPoolCommitted = @PercentOfDesiredSizeMB
        , BatchReqPerSecond = (@BatchReq2 - @BatchReq1) / @NumSeconds
        , LazyWritesPerSecond = (@LazyWrites2 - @LazyWrites1) / @NumSeconds
        , FreeListStallsPerSecond = (@FreeListStallsSec2 - @FreeListStallsSec2) / @NumSeconds
        /*, VMType = @VMType*/
        , IOTaskCount = @TaskCount 
        , TaskPerSecondAvg = @TasksPerSecondAvg 
        , AvgWaitTimeInMSPerTask = @AvgWaitTimeInMSPerTask 
        , AvgWaitTimeInMSPerSecond = @AvgWaitTimeInMSPerSecond 
        , TotalWaitTimeInMSOverall  = @TotalWaitTimeInMSOverall
        , SamplePeriodinSec = @NumSeconds;

    SELECT MaxServerMemorySuggested = 
            CASE WHEN @BufferPoolCommittedMB < @BufferPoolCommitTargetMB 
            THEN @BufferPoolCommittedMB 
            ELSE ((CONVERT(DECIMAL(18,4), @MinPLE) / @PLESeconds) * @MaxServerMemory) 
                    + (((@LazyWrites2 - @LazyWrites1) / @NumSeconds) * 64) 
                    + ((@FreeListStallsSec2 - @FreeListStallsSec2) / @NumSeconds) * 64 
            END
        , Reason = CASE WHEN @BufferPoolCommittedMB < @BufferPoolCommitTargetMB THEN N'Committed MB less than current Max Server Memory'
            ELSE N'Calculated based on PLE, Lazy Writes / second and List Stalls / second' END
        , LazyWritesX64 = (((@LazyWrites2 - @LazyWrites1) / @NumSeconds) * 64)
        , ListStallsX64 = ((@FreeListStallsSec2 - @FreeListStallsSec2) / @NumSeconds) * 64;
END

DECLARE @Out TABLE
(
    KeyID INT IDENTITY(1,1)
    , ItemDesc NVARCHAR(255)
    , ItemValue SQL_VARIANT
    , IsDebug BIT DEFAULT(0)
);

INSERT INTO @Out (ItemDesc, ItemValue, IsDebug)
VALUES (N'Server Name', CONVERT(NVARCHAR(255),@@SERVERNAME), 1);

INSERT INTO @Out (ItemDesc, ItemValue)
VALUES (N'Data Space Used (MB)', @SpaceUsedInMB);

INSERT INTO @Out (ItemDesc, ItemValue)
VALUES (N'Page Life Expectancy (sec)', @PLESeconds);

INSERT INTO @Out (ItemDesc, ItemValue)
VALUES (N'Minimum Acceptable Page Life Expectancy (sec)', @MinPLE);

INSERT INTO @Out (ItemDesc, ItemValue)
VALUES (N'Minimum Server Memory (MB)', @MinServerMemory);

INSERT INTO @Out (ItemDesc, ItemValue)
VALUES (N'Maximum Server Memory (MB)', @MaxServerMemory);

INSERT INTO @Out (ItemDesc, ItemValue)
VALUES (N'Total Server RAM in MB', @RamMB);

INSERT INTO @Out (ItemDesc, ItemValue)
VALUES (N'Buffer Pool Committed MB', @BufferPoolCommittedMB);

INSERT INTO @Out (ItemDesc, ItemValue)
VALUES (N'Buffer Pool Commit Target MB', @BufferPoolCommitTargetMB);

INSERT INTO @Out (ItemDesc, ItemValue)
VALUES (N'Percent of Buffer Pool Committed', @PercentOfDesiredSizeMB);

INSERT INTO @Out (ItemDesc, ItemValue)
VALUES (N'Batch Requests Per Second', (@BatchReq2 - @BatchReq1) / @NumSeconds);

INSERT INTO @Out (ItemDesc, ItemValue)
VALUES (N'Lazy Writes Per Second', (@LazyWrites2 - @LazyWrites1) / @NumSeconds);

INSERT INTO @Out (ItemDesc, ItemValue)
VALUES (N'Free List Stalls Per Second', (@FreeListStallsSec2 - @FreeListStallsSec2) / @NumSeconds);

INSERT INTO @Out (ItemDesc, ItemValue)
VALUES (N'IO Task Count', @TaskCount);

INSERT INTO @Out (ItemDesc, ItemValue)
VALUES (N'Task Per Second Avg', @TasksPerSecondAvg);

INSERT INTO @Out (ItemDesc, ItemValue)
VALUES (N'Avg Wait Time In MS Per Task', @AvgWaitTimeInMSPerTask);

INSERT INTO @Out (ItemDesc, ItemValue)
VALUES (N'Avg Wait Time In MS Per Second', @AvgWaitTimeInMSPerSecond);

INSERT INTO @Out (ItemDesc, ItemValue)
VALUES (N'Total Wait Time In MS Overall', @TotalWaitTimeInMSOverall);

INSERT INTO @Out (ItemDesc, ItemValue)
VALUES (N'Sample Period in Seconds', @NumSeconds);

INSERT INTO @Out (ItemDesc, ItemValue)
VALUES (N'Lazy Writes per Second', ((@LazyWrites2 - @LazyWrites1) / @NumSeconds));

INSERT INTO @Out (ItemDesc, ItemValue)
VALUES (N'List Stalls per Second', ((@FreeListStallsSec2 - @FreeListStallsSec2) / @NumSeconds));

INSERT INTO @Out (ItemDesc, ItemValue)
VALUES (N'Recommended Max Memory (MB)', N'');

INSERT INTO @Out (ItemDesc, ItemValue)
VALUES (N'Recommended Max Memory Reason', N'');

INSERT INTO @Out (ItemDesc, ItemValue, IsDebug)
VALUES (N'Recommended Max Memory Signal', 0, 1);

/*
    Add memory if Lazy Writes occurred
    Add 64MB per Lazy Write (just for fun)
*/
DECLARE @LazyWritesMB INT;
SET @LazyWritesMB = (((@LazyWrites2 - @LazyWrites1) / @NumSeconds) * 64);

/*
    Add memory if Free List Stalls occurred
    Add 128MB per Free List Stall
*/
DECLARE @FreeListStallMB INT;
SET @FreeListStallMB = (((@FreeListStallsSec2 - @FreeListStallsSec2) / @NumSeconds) * 128);

/*
    Add the Additional memory requirements to the Recommended Max Memory row
*/
DECLARE @AdditionalMemory INT;
SET @AdditionalMemory = 
    @LazyWritesMB
    + @FreeListStallMB;

IF (@MaxServerMemory + @AdditionalMemory < 1024) AND (@PLESeconds >= @MinPLE)
BEGIN
    UPDATE @Out 
    SET ItemValue = @MaxServerMemory
    WHERE ItemDesc = N'Recommended Max Memory (MB)';

    UPDATE @Out 
    SET ItemValue = 'Max Server Memory is low, however PLE is acceptable'
    WHERE ItemDesc = N'Recommended Max Memory Reason';

    UPDATE @Out 
    SET ItemValue = 1
    WHERE ItemDesc = N'Recommended Max Memory Signal';
END

IF ((@BufferPoolCommittedMB + @AdditionalMemory) < @BufferPoolCommitTargetMB) AND (@PLESeconds >= @MinPLE)
BEGIN
    UPDATE @Out 
    SET ItemValue = @BufferPoolCommittedMB + @AdditionalMemory
    WHERE ItemDesc = N'Recommended Max Memory (MB)';

    UPDATE @Out 
    SET ItemValue = 'Buffer pool committed is less than Max Server Memory, and PLE is acceptable.'
    WHERE ItemDesc = N'Recommended Max Memory Reason';

    UPDATE @Out 
    SET ItemValue = 2
    WHERE ItemDesc = N'Recommended Max Memory Signal';
END

DECLARE @PLEMultiplier DECIMAL(10,2);
SET @PLEMultiplier = (CONVERT(DECIMAL(10,2),@MinPLE) / CONVERT(DECIMAL(10,2), @PLESeconds));
IF @PLEMultiplier < 0.90 SET @PLEMultiplier = 0.90;
IF @PLEMultiplier > 1.10 SET @PLEMultiplier = 1.10;

INSERT INTO @Out (ItemDesc, ItemValue, IsDebug)
VALUES (N'PLE Multiplier', @PLEMultiplier, 1);

IF /*(@MaxServerMemory + @AdditionalMemory >= 1024) AND*/ (@PLESeconds <= @MinPLE)
BEGIN
    UPDATE @Out 
    SET ItemValue = 
        (SELECT TOP(1) Inc
        FROM (
            SELECT Inc = t.RowNum * 256
            FROM (
                SELECT RowNum = CONVERT(BIGINT,ROW_NUMBER() OVER (ORDER BY o.object_id))
                FROM sys.objects o, sys.objects o1
                ) t
            WHERE (t.RowNum * 256) <  CONVERT(BIGINT,POWER(2,30))
            ) t1
        WHERE t1.Inc > CONVERT(INT, (@MaxServerMemory * @PLEMultiplier))
        ORDER BY t1.Inc)
    WHERE ItemDesc = N'Recommended Max Memory (MB)';

    UPDATE @Out 
    SET ItemValue = 'Low PLE indicates Max Server Memory should be adjusted upwards.'
    WHERE ItemDesc = N'Recommended Max Memory Reason';

    UPDATE @Out 
    SET ItemValue = 3
    WHERE ItemDesc = N'Recommended Max Memory Signal';
END

IF (@MaxServerMemory + @AdditionalMemory >= 1024) AND (@PLESeconds > @MinPLE)
BEGIN
    UPDATE @Out 
    SET ItemValue = 
        (SELECT TOP(1) Inc
        FROM (
            SELECT Inc = t.RowNum * 256
            FROM (
                SELECT RowNum = CONVERT(BIGINT,ROW_NUMBER() OVER (ORDER BY o.object_id))
                FROM sys.objects o, sys.objects o1
                ) t
            WHERE (t.RowNum * 256) <  CONVERT(BIGINT,POWER(2,30))
            ) t1
        WHERE t1.Inc <= CONVERT(INT, (@MaxServerMemory * @PLEMultiplier))
        ORDER BY t1.Inc DESC)
    WHERE ItemDesc = N'Recommended Max Memory (MB)';

    UPDATE @Out 
    SET ItemValue = 'High PLE indicates Max Server Memory could be adjusted downwards.'
    WHERE ItemDesc = N'Recommended Max Memory Reason';

    UPDATE @Out 
    SET ItemValue = 4
    WHERE ItemDesc = N'Recommended Max Memory Signal';
END

DECLARE @RecommendedMaxServerMemory INT;
SELECT  @RecommendedMaxServerMemory = CONVERT(INT,ItemValue)
FROM @Out o 
WHERE o.ItemDesc = N'Recommended Max Memory (MB)';

IF @RecommendedMaxServerMemory > (@MaxServerMemory * 0.96)
    AND @RecommendedMaxServerMemory < (@MaxServerMemory * 1.04)
BEGIN
    UPDATE @Out
    SET ItemValue = @MaxServerMemory
    WHERE ItemDesc = N'Recommended Max Memory (MB)';
    UPDATE @Out
    SET ItemValue = 'No changed recommended'
    WHERE ItemDesc = N'Recommended Max Memory Reason';
    UPDATE @Out 
    SET ItemValue = 0
    WHERE ItemDesc = N'Recommended Max Memory Signal';
END 

IF (@HTMLOutput = 1)
BEGIN
    SELECT ItemValue
        , HTMLOutput = '<table>' + 
            (
                SELECT 'td' = ItemDesc
                    , ''
                    , 'td' = ItemValue
                    , ''
                FROM @Out o
                WHERE CASE WHEN @Debug = 0 THEN o.IsDebug ELSE 0 END = 0
                ORDER BY o.KeyID
                FOR XML PATH('tr')
            ) +
            '</table>'
    FROM @Out o
    WHERE o.ItemDesc = N'Recommended Max Memory Signal';
END
ELSE
BEGIN
    SELECT *
    FROM @Out o
    WHERE CASE WHEN @Debug = 0 THEN o.IsDebug ELSE 0 END = 0
    ORDER BY o.KeyID;
END

Este código compara o PLE com um PLE mínimo "aceitável" pela quantidade de max server memorysistema configurada. Se o PLE for sensivelmente maior que o número aceitável, sugere um máximo de 10% menor max server memory. Se o PLE for menor que o PLE aceitável, sugere um máximo de 10% a mais max server memory.

Se a quantidade real de buffer pool confirmado for menor que o tamanho do buffer pool de destino, é recomendável diminuir max server memorypara essa quantidade, além de alguma memória extra para threads, gravações lentas, etc.

O código também analisa vários contadores de desempenho, como Lazy Writes / second, Free List Stalls e Batch Requests.

O código não é perfeito, estou compartilhando-o aqui para obter informações e para o benefício de futuros usuários de SO.


1
Lembre-se de que, a partir do destino do Buffer Pool do SQL Server 2012 e confirmado, não faz sentido e esses contadores foram descontinuados. Em vez disso, você deve usar o Destino do Gerenciador de Memória Confirmado (KB) e confirmado no momento. Se você quiser ler mais, por que isso dá um valor errado social.technet.microsoft.com/wiki/contents/articles/…
Shanky
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.