Qu'est-ce qu'une méthode déterministe pour évaluer une taille de pool de tampons sensible?

29

J'essaie de trouver une manière saine de comprendre si le max server memory (mb)paramètre est approprié (soit être plus bas, soit plus haut, ou rester tel quel). Je suis conscient que cela max server memory (mb)devrait toujours être suffisamment bas pour laisser de la place au système d'exploitation lui-même, etc.

L'environnement que je regarde compte plusieurs centaines de serveurs; J'ai besoin d'une formule fiable que je peux utiliser pour déterminer si la taille actuelle du pool de tampons est appropriée, car la RAM est calculée par Go alloué à chaque serveur. L'environnement entier est virtualisé et la RAM «physique» allouée à une machine virtuelle peut facilement être modifiée à la hausse ou à la baisse.

J'ai une instance SQL Server particulière que je regarde maintenant avec un PLE de 1 100 052 secondes, ce qui équivaut à 12,7 jours (la durée de fonctionnement du serveur). Le serveur a un paramètre de mémoire maximale du serveur de 2560 Mo (2,5 Go), dont seulement 1380 Mo (1,3 Go) sont réellement engagés.

J'ai lu plusieurs articles dont un de Jonathan Keheyias ( post ) et un autre de Paul Randal ( post ), et plusieurs autres. Jonathan préconise la surveillance d'un PLE inférieur à 300 par 4 Go de pool de mémoire tampon comme étant trop faible. Pour l'instance SQL Server ci-dessus, il en 300 * (2.5 / 4) = 187résulte un PLE cible vraiment très bas en dessous de 300. Cette instance a 290 Go de données SQL Server (sans les fichiers journaux) et n'est utilisée que pour les tests d'intégration. En supposant que les 12 derniers jours sont représentatifs de l'utilisation typique de ce serveur, je dirais que le max server memory (mb)paramètre pourrait être réduit.

À l'autre extrémité de l'échelle, j'ai un autre serveur de test d'intégration avec un PLE de 294, qui a un max server memory (mb)paramètre de seulement 1 Go. Ce serveur ne dispose que de 224 Mo de données SQL Server, journaux non compris, et exécute certaines bases de données BizFlow. Ce serveur peut bénéficier d'un max server memory (mb)paramètre plus élevé .

Je pense qu'un bon point de départ pour les cibles auxquelles on pourrait attribuer trop de mémoire pourrait être de regarder:

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;

Si BufferPoolCommitTargetMB / BufferPoolCommittedMBest supérieur à 1, le serveur n'utilise pas la totalité du pool de tampons. Si la machine en question a également un PLE supérieur à "x", elle pourrait être un bon candidat pour une diminution de max server memory (mb).

Étant donné que le Buffer Manager:Lazy writes/seccompteur de performances suit le nombre de fois que SQLOS a écrit des pages sur le disque entre les points de contrôle en raison de la pression de la mémoire, cela peut être une autre bonne chose à regarder.

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;

Le code ci-dessus suppose que le serveur est sous charge pendant les 15 secondes nécessaires à son exécution, sinon il affichera 0; qui pourrait être un faux négatif trompeur.

Dois-je également consulter les PAGELATCHIO_*statistiques d'attente ou un autre type d'attente comme indicateur de la pression sur la mémoire ou de son absence?

Ma question est, comment puis-je déterminer de manière fiable une "bonne" valeur cible pour PLE et max server memory (mb)?

Max Vernon
la source

Réponses:

11

Comme vous le savez déjà, il n'y a pas de formule générale pour calculer la mémoire maximale du serveur, vous pouvez faire quelques calculs rapides et atteindre une valeur, mais vous aurez enfin besoin de l'aide de compteurs Perfmon pour surveiller l'utilisation de la mémoire et modifier en conséquence. Je connais la formule générale ci-dessous et je l'utilise également. J'ai appris cette formule de This Link

Pour SQL Server 2005 à 2008 R2

Veuillez noter que de SQL Server 2005 à 2008 R2, la mémoire maximale du serveur ne contrôle que le pool de mémoire tampon. La configuration maximale de la mémoire du serveur est donc un peu fastidieuse ici et implique peu de calculs

  1. Laissez immédiatement 2 Go de mémoire pour le système d'exploitation Windows.

  2. Bien sûr, le système aurait un antivirus en cours d'exécution. Veuillez laisser 1.5G pour Antivirus. Veuillez noter que Mcafee et SQL Server ne vont pas de pair, alors assurez-vous d'en laisser suffisamment. Vous pouvez également vérifier le compteur perfmon Perfmon Process-> Private bytes and Working Setpour surveiller l'utilisation de la mémoire par AV et d'autres petites applications exécutées sur SQL Server.

entrez la description de l'image ici

  1. Tenez compte des besoins en mémoire des pilotes / firmwares. Vous devez les dériver en fonction des besoins en mémoire des pilotes installés sur le système. L'outil RAMMAP peut vous aider

  2. Tenez compte des exigences de mémoire NonbPool (aka MTL ou MTR) de SQL Server.

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

    + Nombre maximal de threads de travail * 2 Mo

    + Mémoire pour les allocations Windows directes d'environ 0 à 300 Mo dans la plupart des cas, mais vous devrez peut-être l'augmenter si de nombreux composants tiers sont chargés dans le processus SQL Server (y compris les DLL de serveur lié, les DLL de sauvegarde tierces, etc.)

    + Si vous utilisez CLR de manière extensive, ajoutez de la mémoire supplémentaire pour CLR.

  3. Tenez compte de la mémoire requise par les travaux (y compris les agents de réplication, l'envoi de journaux, etc.) et les packages qui s'exécuteront sur le serveur. Cela peut aller des Mo aux Go en fonction du nombre de travaux en cours d'exécution. Pour un serveur de taille moyenne, vous pouvez le considérer comme 250 Mo

  4. Assurez-vous qu'il y a suffisamment d'espace libre pour le système d'exploitation.

    Environ (100 Mo pour chaque Go jusqu'à 4G) + (50 Mo pour chaque Go supplémentaire jusqu'à 12 Go) + (25 Mo pour chaque Go supplémentaire jusqu'à la taille de votre RAM)

  5. Autres besoins en mémoire.

    Si vous avez d'autres besoins en mémoire spécifiques à votre environnement.

    Mémoire maximale du serveur = mémoire physique totale - (1 + 2 + 3 + 4 + 5 + 6 + 7)

    Je n'ai pas inclus la configuration de la mémoire pour SSIS.SSRS, SSAS, vous devrez également soustraire la mémoire requise par ces services de la mémoire totale du serveur physique.

    Après avoir configuré ci-dessus, vous devez surveiller les compteurs suivants

  • SQLServer: Buffer Manager - Espérance de vie de la page (PLE):

  • SQLServer: Buffer Manager - CheckpointPages / sec:

  • SQLServer: Gestionnaire de mémoire - Subventions de mémoire en attente:

  • SQLServer: gestionnaire de mémoire - Mémoire du serveur cible:

  • SQLServer: Memory Manager - Mémoire totale du serveur

Pour SQL Server 2012/2014.

From SQL Server 2012 onwardsla configuration de la mémoire maximale du serveur est devenue facile. Parce que maintenant, la mémoire maximale du serveur représente presque toute la consommation de mémoire. La mémoire maximale du serveur contrôle l'allocation de mémoire SQL Server, y compris le pool de mémoire tampon, la mémoire de compilation, tous les caches, les allocations de mémoire qe, la mémoire du gestionnaire de verrous et la mémoire CLR (essentiellement tout «commis» tel que trouvé dans dm_os_memory_clerks). La mémoire pour les piles de threads, les tas, les fournisseurs de serveurs liés autres que SQL Server ou toute mémoire allouée par une DLL «non SQL Server» n'est pas contrôlée par la mémoire maximale du serveur.

Vous pouvez allouer 75 à 80% à SQL Server, puis utiliser des compteurs perfmon pour surveiller l'utilisation de la mémoire. Dans SQL Server 2012, quelques compteurs perfmon ont été déconseillés. Le compteur du gestionnaire de mémoire tampon est obsolète, vous devez utiliser le compteur du gestionnaire de mémoire

  • SQL Server: Gestionnaire de mémoire - Mémoire du serveur cible (Ko)

  • SQL Server: Gestionnaire de mémoire - Mémoire totale du serveur (Ko)

  • SQL Server: Gestionnaire de mémoire - Mémoire libre (Ko)

  • SQL Server: Gestionnaire de mémoire - Mémoire cache de base de données (Ko)

Sur la valeur du PLE, j'ai utilisé la formule de Joanthan et heureusement, cela a fonctionné pour moi.

Shanky
la source
6

Le défi ici est que les chiffres ne prennent pas en compte l'expérience de l'utilisateur final.

Excellent exemple: j'ai un serveur de base de données utilisé pour suivre chaque site Web visité par les employés de l'entreprise. Je m'en fiche si elle ne peut pas suivre les insertions pendant les pics de charge, car l'application frontale regroupe les insertions périodiquement, et les insertions lentes ne causent pas de problème aux utilisateurs. Les utilisateurs peuvent toujours surfer sur le Web sans être bloqués par des insertions lentes.

À l'heure de SELECT, le service des ressources humaines déclenche simplement des rapports lorsqu'on lui demande un historique de navigation suspect pour un employé donné, mais ils ne se soucient pas de la durée des rapports - ils ouvrent simplement le rapport et partent pour d'autres choses.

Les performances doivent commencer par une question: les utilisateurs sont-ils satisfaits des performances? Si oui, laissez le système où il se trouve.

Brent Ozar
la source
Même si vous utilisez plus de mémoire que nécessaire?
James Anderson
2
James - d'une manière générale, je ne veux pas apporter de modifications qui poussent les utilisateurs à se plaindre. Si vous voulez le faire, vous pouvez progressivement réduire la quantité de mémoire pour chaque serveur jusqu'à ce que les utilisateurs commencent à se plaindre, mais lorsque je suis déjà surchargé de travail, je n'ai généralement pas le temps de prendre ces mesures. Je dois me concentrer sur les tâches qui rendront les utilisateurs mécontents heureux - plutôt que d'essayer de rendre les utilisateurs heureux mécontents. ;-)
Brent Ozar
2
De bons points, Brent. On m'a demandé de voir si certains serveurs sont sur-provisionnés car nous payons la mémoire par Go par an. Beaucoup des cas que je regarde ont ce que je considère comme une très petite quantité de RAM max server memory (mb), et en tant que tel, je suis assez réticent à les réduire. Cependant, certaines autres instances ont 1000000 + PLE, et en tant que telles sont des candidats potentiels assez évidents pour une baisse de RAM. De toute évidence, l' abaissement RAM entraînera une augmentation IOps, et je ne suis pas sûr de ce que le coût de ce sera.
Max Vernon
1
En outre, regarder PLE par rapport au max server memorycadre est une sorte de chose avec du poulet et des œufs; plus le max server memoryréglage est bas , plus le PLE minimum "acceptable" serait bas, donc je pourrais rester coincé dans une spirale toujours plus basse. Je suis sûr, comme vous le mentionnez, que les performances des utilisateurs seront à un moment donné affectées.
Max Vernon
Les compteurs PLE sont ceux que vous devriez éviter de chercher à partir de 2012 ou lorsque vous avez un système NUMA où chaque nœud se comporte comme son propre petit allocateur de mémoire. Si vous voulez, vous devez rechercher le PLE pour chaque nœud NUMA non complet, vous pourriez obtenir une valeur incorrecte
Shanky
3

Le T-SQL actuel que j'utilise pour évaluer PLE vs max server memoryest:

/*
    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

Ce code compare le PLE à un PLE "acceptable" minimum pour la quantité de max server memorysystème configurée. Si le PLE est sensiblement supérieur au nombre acceptable, il suggère un maximum de 10% inférieur max server memory. Si le PLE est inférieur au PLE acceptable, il suggère un maximum de 10% de plus max server memory.

Si la quantité réelle de pool de mémoire tampon validée est inférieure à la taille de pool de mémoire tampon cible, elle suggère de réduire max server memorycette quantité, ainsi que de la mémoire supplémentaire pour les threads, les écritures différées, etc.

Le code examine également divers compteurs de performances pour des choses comme les écritures paresseuses / seconde, les décrochages de liste gratuits et les demandes de lots.

Le code n'est pas parfait, je le partage ici pour obtenir des commentaires et pour le bénéfice des futurs utilisateurs de SO.

Max Vernon
la source
1
Max Mind vous à partir de la cible du pool de tampons SQL Server 2012 et engagé n'a aucun sens et ces compteurs sont déconseillés. Au lieu de cela, vous devez utiliser Memory Manager Target Committed (KB) et current commit. Si vous voulez en savoir plus pourquoi il donne une valeur erronée social.technet.microsoft.com/wiki/contents/articles/...
Shanky