Jaka jest deterministyczna metoda oceny rozsądnej wielkości puli buforów?

29

Próbuję wymyślić rozsądny sposób, aby zrozumieć, czy max server memory (mb)ustawienie jest odpowiednie (albo powinno być niższe, albo wyższe, albo pozostać takie, jakie jest). Wiem, że max server memory (mb)zawsze powinna być wystarczająco niska, aby pozostawić miejsce dla samego systemu operacyjnego itp.

Środowisko, na które patrzę, ma kilkaset serwerów; Potrzebuję niezawodnej formuły, której mogę użyć, aby ustalić, czy obecny rozmiar puli buforów jest odpowiedni, ponieważ pamięć RAM jest naliczana za GB przydzielony każdemu serwerowi. Całe środowisko jest zwirtualizowane, a „fizyczną” pamięć RAM przydzieloną maszynie wirtualnej można łatwo zmienić w górę lub w dół.

Mam konkretną instancję serwera SQL, na którą teraz patrzę, z PLE równym 11000552 sekund, co odpowiada 12,7 dniom (czas działania serwera). Serwer ma maksymalne ustawienie pamięci serwera na 2560 MB (2,5 GB), z czego faktycznie tylko 1380 MB (1,3 GB) jest faktycznie zatwierdzone.

Przeczytałem kilka pozycji, w tym jedną autorstwa Jonathana Keheyiasa ( post ), drugą Paula Randala ( post ) i kilka innych. Jonathan opowiada się za zbyt niskim poziomem PLE poniżej 300 na 4 GB puli buforów. W przypadku powyższej instancji programu SQL Server 300 * (2.5 / 4) = 187powoduje to naprawdę bardzo niski docelowy poziom PLE poniżej 300. Ta instancja zawiera 290 GB danych programu SQL Server (bez plików dziennika) i służy wyłącznie do testowania integracji. Zakładając, że ostatnie 12 dni jest typowe dla tego serwera, powiedziałbym, że max server memory (mb)ustawienie można obniżyć.

Na drugim końcu skali mam inny serwer testowy integracji z PLE 294, który ma max server memory (mb)ustawienie tylko 1 GB. Ten serwer ma tylko 224 MB danych SQL Server, nie wliczając dzienników, i obsługuje niektóre bazy danych BizFlow. Ten serwer może skorzystać z wyższego max server memory (mb)ustawienia.

Myślę, że dobrym miejscem początkowym dla celów, którym można przypisać zbyt dużo pamięci , może być spojrzenie na:

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;

Jeśli BufferPoolCommitTargetMB / BufferPoolCommittedMBjest większa niż 1, serwer nie używa całej puli buforów. Jeśli dana maszyna ma również PLE większą niż „x”, może to być dobry kandydat na zmniejszenie max server memory (mb).

Ponieważ Buffer Manager:Lazy writes/seclicznik wydajności śledzi, ile razy SQLOS zapisywał strony na dysk między punktami kontrolnymi z powodu presji pamięci, może to być kolejna dobra rzecz do obejrzenia.

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;

Powyższy kod zakłada, że ​​serwer jest obciążony w ciągu 15 sekund potrzebnych do uruchomienia, w przeciwnym razie zgłosi 0; co może wprowadzać w błąd fałszywie ujemny.

Czy powinienem także patrzeć na PAGELATCHIO_*statystyki oczekiwania lub inny typ oczekiwania jako wskaźnik presji pamięci, czy jej braku?

Moje pytanie brzmi: w jaki sposób mogę wiarygodnie określić „dobrą” wartość docelową dla PLE i max server memory (mb)?

Max Vernon
źródło

Odpowiedzi:

11

Jak już wiesz, nie istnieje ogólna formuła do obliczania maksymalnej pamięci serwera, możesz zrobić kilka szybkich obliczeń matematycznych i osiągnąć wartość, ale nadal potrzebujesz pomocy liczników Perfmon, aby monitorować użycie pamięci i odpowiednio ją zmienić. Znam poniżej ogólną formułę i też jej używam. Nauczyłem się tej formuły od This Link

Dla SQL Server 2005 do 2008 R2

Uwaga: od SQL Server 2005 do 2008 R2 maksymalna pamięć serwera kontroluje tylko pulę buforów. Tak więc maksymalna konfiguracja pamięci serwera jest nieco nudna i wymaga kilku obliczeń

  1. Pozostaw 2 G pamięci od razu dla systemu operacyjnego Windows.

  2. Oczywiście w systemie działałby program antywirusowy. Pozostaw 1,5G dla programu antywirusowego. Pamiętaj, że Mcafee i SQL Server nie idą w parze, więc upewnij się, że zostawiłeś wystarczająco dużo na to. Możesz także sprawdzić licznik perfmon, Perfmon Process-> Private bytes and Working Setaby monitorować użycie pamięci przez AV i inne małe aplikacje działające na SQL Server

wprowadź opis zdjęcia tutaj

  1. Weź pod uwagę wymagania dotyczące sterowników sterowników / oprogramowania układowego. Musisz je uzyskać na podstawie wymagań dotyczących pamięci sterowników zainstalowanych w systemie. Narzędzie RAMMAP może pomóc

  2. Rozważ wymagania dotyczące pamięci NonbPool (inaczej MTL lub MTR) przez SQL Server.

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

    + Maksymalna liczba wątków roboczych * 2 MB

    + Pamięć do bezpośrednich przydziałów systemu Windows w przybliżeniu od 0 do 300 MB w większości przypadków, ale może być konieczne jej zwiększenie, jeśli w procesie SQL Server załadowanych jest wiele składników 3-stronnych (w tym dll połączonych serwerów, dll kopii zapasowych stron trzecich itp.)

    + Jeśli używasz CLR, dodaj trochę dodatkowej pamięci dla CLR.

  3. Rozważ wymagania dotyczące pamięci według zadań (w tym agentów replikacji, wysyłania dziennika itp.) I pakietów, które będą działać na serwerze. Może bardzo od MB do GB w zależności od liczby uruchomionych zadań. W przypadku średnich serwerów możesz wziąć to jako 250 MB

  4. Upewnij się, że jest wystarczająco dużo wolnego miejsca dla systemu operacyjnego.

    Około (100 MB na każde GB do 4G) + (50 MB na każde dodatkowe GB do 12 GB) + (25 MB na każde dodatkowe GB do wielkości pamięci RAM)

  5. Inne wymagania dotyczące pamięci.

    Jeśli masz inne wymagania dotyczące pamięci specyficzne dla twojego środowiska.

    Maksymalna pamięć serwera = Całkowita pamięć fizyczna - (1 + 2 + 3 + 4 + 5 + 6 + 7)

    Nie podałem konfiguracji pamięci dla SSIS.SSRS, SSAS należy również odjąć pamięć wymaganą przez te usługi od całkowitej pamięci serwera fizycznego.

    Po skonfigurowaniu powyżej musisz monitorować następujące liczniki

  • SQLServer: Menedżer buforów - Przewidywana trwałość strony (PLE):

  • SQLServer: Menedżer buforów - CheckpointPages / sec:

  • SQLServer: Menedżer pamięci - przyznanie pamięci w toku:

  • SQLServer: Menedżer pamięci - Pamięć serwera docelowego:

  • SQLServer: menedżer pamięci - pamięć serwera ogółem

Dla SQL Server 2012/2014.

From SQL Server 2012 onwardsustawienie maksymalnej pamięci serwera stało się łatwe. Ponieważ teraz maksymalna pamięć serwera prawie odpowiada za całe zużycie pamięci. Maksymalna pamięć serwera kontroluje alokację pamięci SQL Server, w tym pulę buforów, pamięć kompilacji, wszystkie pamięci podręczne, przydzielanie pamięci qe, pamięć menedżera blokad i pamięć CLR (w zasadzie dowolny „urzędnik” znaleziony w dm_os_memory_clerks). Pamięć stosów wątków, hałd, połączonych dostawców serwerów innych niż SQL Server lub dowolna pamięć przydzielona przez bibliotekę DLL „inną niż SQL Server” nie jest kontrolowana przez maksymalną pamięć serwera.

Możesz przydzielić 75–80% do SQL Server, a następnie użyć liczników perfmon do monitorowania zużycia pamięci. W SQL Server 2012 kilka liczników perfmon zostało uznanych za przestarzałe. Licznik menedżera buforów jest przestarzały, należy użyć licznika menedżera pamięci

  • SQL Server: Menedżer pamięci - docelowa pamięć serwera (KB)

  • SQL Server: Menedżer pamięci - Całkowita pamięć serwera (KB)

  • SQL Server: Menedżer pamięci - wolna pamięć (KB)

  • SQL Server: Menedżer pamięci - pamięć podręczna bazy danych (KB)

Jeśli chodzi o wartość PLE, użyłem formuły Joanthana i na szczęście to zadziałało dla mnie.

Shanky
źródło
6

Wyzwanie polega na tym, że liczby nie uwzględniają wrażeń użytkowników końcowych.

Świetny przykład: mam serwer bazy danych używany do śledzenia każdej witryny odwiedzanej przez pracowników firmy. Nie obchodzi mnie, czy nie może nadążyć za wstawkami podczas szczytowych obciążeń, ponieważ aplikacja frontonu okresowo wyłącza wkładki, a powolne wkładki nie powodują problemów dla użytkowników. Użytkownicy nadal mogą surfować po Internecie bez powolnego wstawiania.

W czasie SELECT dział HR po prostu odpala raporty, gdy zostaje poproszony o podejrzaną historię przeglądania dla danego pracownika, ale nie obchodzi ich, ile czasu zajmują raporty - po prostu otwierają raport i robią inne rzeczy.

Wydajność musi zaczynać się od pytania: czy użytkownicy są zadowoleni z wydajności? Jeśli tak, zostaw system tam, gdzie jest.

Brent Ozar
źródło
Nawet jeśli używasz więcej pamięci, niż potrzebujesz?
James Anderson,
2
James - ogólnie rzecz biorąc, nie chcę dokonywać zmian, które powodują, że użytkownicy narzekają. Jeśli chcesz to zrobić, możesz stopniowo zmniejszyć ilość pamięci dla każdego serwera, aż użytkownicy zaczną narzekać, ale kiedy już jestem przepracowany, zazwyczaj nie mam czasu, aby podjąć te kroki. Muszę skupić się na zadaniach, które zadowolą nieszczęśliwych użytkowników - zamiast starać się, aby zadowoleni użytkownicy byli nieszczęśliwi. ;-)
Brent Ozar,
2
Dobre punkty, Brent. Zostałem poproszony o sprawdzenie, czy niektóre serwery są przepełnione, ponieważ płacimy za pamięć za GB rocznie. Wiele przypadków, na które patrzę, ma coś, co uważam za bardzo małą ilość pamięci RAM max server memory (mb), i dlatego raczej nie chcę ich zmniejszać. Jednak niektóre inne instancje mają ponad 1 000 000 PLE i jako takie są dość oczywistymi potencjalnymi kandydatami na spadek pamięci RAM. Oczywiście, obniżenie RAM spowoduje wzrost IOPS, a nie jestem pewien, co koszt to będzie.
Max Vernon,
1
Również patrzenie na PLE w porównaniu z max server memoryotoczeniem jest czymś w rodzaju kurczaka i jajka; im niższe max server memoryustawienie, tym niższy byłby minimalny „akceptowalny” poziom PLE, więc mogłem utknąć w coraz niższej spirali. Jestem pewien, że, jak wspomniałeś, w pewnym momencie wpłynie to na wydajność użytkownika .
Max Vernon,
Liczniki PLE to takie, których należy unikać od 2012 r. Lub gdy masz system NUMA, w którym każdy węzeł zachowuje się jak własny mały alokator pamięci. Jeśli chcesz, powinieneś poszukać PLE dla każdego niekompletnego węzła NUMA, możesz otrzymać niepoprawną wartość
Shanky
3

Obecny T-SQL, którego używam do oceny PLE vs max server memoryto:

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

Ten kod porównuje PLE z minimalnym „akceptowalnym” PLE dla ilości max server memoryskonfigurowanej przez system. Jeśli poziom PLE jest znacznie wyższy niż dopuszczalna liczba, sugeruje to maksymalnie 10% mniej max server memory. Jeśli poziom PLE jest niższy niż dopuszczalny poziom PLE, sugeruje to maksymalnie 10% więcej max server memory.

Jeśli rzeczywista ilość zatwierdzonej puli buforów jest mniejsza niż docelowa wielkość puli buforów, sugeruje to obniżenie max server memorydo tej ilości, a także trochę dodatkowej pamięci dla wątków, leniwych zapisów itp.

Kod analizuje także różne liczniki wydajności, takie jak Lazy Writes / second, Free List Stalls i Batch Requests.

Kod nie jest idealny, udostępniam go tutaj, aby uzyskać dane wejściowe i z korzyścią dla przyszłych użytkowników SO.

Max Vernon
źródło
1
Max Mind, zaczynając od docelowej i zatwierdzonej puli buforów SQL Server 2012, nie ma sensu, a te liczniki są przestarzałe. Zamiast tego należy użyć zatwierdzonego celu menedżera pamięci (KB) i bieżącego zatwierdzenia. Jeśli chcesz dowiedzieć się więcej, dlaczego daje niewłaściwą wartość social.technet.microsoft.com/wiki/contents/articles/…
Shanky