Что такое детерминированный метод для оценки разумного размера пула буферов?

29

Я пытаюсь придумать разумный способ понять, max server memory (mb)подходит ли настройка (должна быть ниже или выше, или не изменяться). Я знаю, что max server memory (mb)всегда должно быть достаточно низким, чтобы оставить место для самой операционной системы и т. Д.

Среда, на которую я смотрю, имеет несколько сотен серверов; Мне нужна надежная формула, которую я могу использовать, чтобы определить, подходит ли текущий размер пула буферов, поскольку ОЗУ стоит на ГБ, выделенный каждому серверу. Вся среда виртуализирована, и «физическая» оперативная память, выделенная виртуальной машине, может быть легко изменена вверх или вниз.

У меня есть конкретный экземпляр SQL Server, на который я сейчас смотрю, с PLE 1100 052 секунд, что соответствует 12,7 дням (время работы сервера). Сервер имеет максимальный параметр памяти сервера 2560 МБ (2,5 ГБ), из которых только 1380 МБ (1,3 ГБ) фактически выделено.

Я прочитал несколько статей, в том числе один от Джонатана Кейхия ( пост ), другой от Пола Рэндала ( пост ) и несколько других. Джонатан рекомендует, чтобы мониторинг PLE ниже 300 на 4 ГБ пула буферов был слишком низким. Для экземпляра SQL Server, приведенного выше, 300 * (2.5 / 4) = 187приводит к действительно очень низкому целевому значению PLE ниже 300. Этот экземпляр имеет 290 ГБ данных SQL Server (не включая файлы журналов) и используется только для интеграционного тестирования. Предполагая, что последние 12 дней представляют типичное использование для этого сервера, я бы сказал, что max server memory (mb)настройка может быть снижена.

На другом конце шкалы у меня есть другой сервер тестирования интеграции с PLE 294, который имеет max server memory (mb)настройку только 1 ГБ. Этот сервер имеет только 224 МБ данных SQL Server, не включая журналы, и работает с некоторыми базами данных BizFlow. Этот сервер может выиграть от более высокой max server memory (mb)настройки.

Я думаю, что хорошей отправной точкой для целей, которым может быть выделено слишком много памяти, может стать просмотр:

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;

Если BufferPoolCommitTargetMB / BufferPoolCommittedMBбольше 1, сервер не использует весь пул буферов. Если рассматриваемая машина также имеет PLE больше «x», то это может быть хорошим кандидатом для уменьшения max server memory (mb).

Поскольку Buffer Manager:Lazy writes/secсчетчик производительности отслеживает, сколько раз SQLOS записывал страницы на диск между контрольными точками из-за нехватки памяти, это может быть еще одним хорошим моментом, на который стоит обратить внимание.

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;

Приведенный выше код предполагает, что сервер находится под нагрузкой в ​​течение 15 секунд, необходимых для запуска, в противном случае он выдаст 0; что может быть ложным отрицательным обманом.

Должен ли я также рассматривать PAGELATCHIO_*статистику ожидания или какой-либо другой тип ожидания как индикатор нехватки памяти или ее отсутствия?

У меня вопрос, как я могу надежно определить «хорошее» целевое значение для PLE и max server memory (mb)?

Макс Вернон
источник

Ответы:

11

Как вы уже знаете, не существует общей формулы для расчета максимального объема памяти сервера, вы можете быстро подсчитать и достичь значения, но вам все равно понадобится помощь счетчиков Perfmon, чтобы отслеживать использование памяти и вносить соответствующие изменения. Я знаю ниже общую формулу, и я использую это также. Я узнал эту формулу из этой ссылки

Для SQL Server 2005 до 2008 R2

Обратите внимание, что с SQL Server 2005 до 2008 R2 максимальная память сервера контролирует только пул буферов. Таким образом, максимальная конфигурация памяти сервера здесь немного утомительна и требует нескольких вычислений.

  1. Оставьте 2 Гб памяти для ОС Windows.

  2. Конечно, в системе должен быть запущен антивирус. Пожалуйста, оставьте 1.5G для антивируса. Обратите внимание, что Mcafee и SQL Server не идут рука об руку, поэтому убедитесь, что вы оставили достаточно для этого. Вы также можете проверить счетчик perfmon, Perfmon Process-> Private bytes and Working Setчтобы отслеживать использование памяти AV и другими небольшими приложениями, запущенными на SQL Server.

введите описание изображения здесь

  1. Примите во внимание требования к памяти драйверов / прошивок. Вы должны получить ее на основе требований к памяти драйверов, установленных в системе. Инструмент RAMMAP может помочь

  2. Рассмотрим требования к памяти NonbPool (иначе MTL или MTR) для SQL Server.

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

    + Максимальное количество рабочих потоков * 2 МБ

    + Память для прямого выделения Windows в большинстве случаев составляет примерно от 0 до 300 МБ, но вам, возможно, придется увеличить ее, если в процесс SQL Server загружено много сторонних компонентов (включая связанные DLL-библиотеки серверов, резервные библиотеки DLL сторонних производителей и т. Д.)

    + Если вы используете CLR, добавьте немного дополнительной памяти для CLR.

  3. Учитывайте требования к памяти для заданий (включая агенты репликации, доставку журналов и т. Д.) И пакетов, которые будут запускаться на сервере. Может варьироваться от МБ до ГБ в зависимости от количества запущенных заданий. Для сервера среднего размера вы можете принять его за 250 МБ

  4. Убедитесь, что для операционной системы достаточно свободного места.

    Примерно (100 МБ на каждый ГБ до 4 ГБ) + (50 МБ на каждый дополнительный ГБ до 12 ГБ) + (25 МБ на каждый дополнительный ГБ до объема ОЗУ)

  5. Другие требования к памяти.

    Если у вас есть другие требования к памяти, специфичные для вашей среды.

    Максимальная память сервера = Общая физическая память - (1 + 2 + 3 + 4 + 5 + 6 + 7)

    Я не включил конфигурацию памяти для SSIS.SSRS, SSAS, вам также необходимо вычесть память, требуемую этими службами, из общей физической памяти сервера.

    После того, как вы настроили выше, вам нужно отслеживать следующие счетчики

  • SQLServer: менеджер буфера - ожидаемая продолжительность жизни страницы (PLE):

  • SQLServer: менеджер буфера - CheckpointPages / sec:

  • SQLServer: диспетчер памяти - ожидают предоставления памяти:

  • SQLServer: диспетчер памяти - память целевого сервера:

  • SQLServer: диспетчер памяти - общий объем памяти сервера

Для SQL Server 2012/2014.

From SQL Server 2012 onwardsнастроить максимальную память сервера стало легко. Потому что теперь максимальная память сервера почти учитывает все потребление памяти. Максимальная память сервера управляет выделением памяти SQL Server, включая пул буферов, память компиляции, все кэши, предоставление памяти qe, память менеджера блокировок и память CLR (в основном любой «клерк», как указано в dm_os_memory_clerks). Память для стеков потоков, кучи, связанных поставщиков серверов, отличных от SQL Server, или любая память, выделенная DLL «не SQL Server», не контролируется максимальной памятью сервера.

Вы можете выделить 75-80% для SQL Server, а затем использовать счетчики perfmon для мониторинга использования памяти. В SQL Server 2012 несколько счетчиков perfmon устарели. Счетчик диспетчера буфера устарел, вы должны использовать счетчик диспетчера памяти

  • SQL Server: диспетчер памяти - память целевого сервера (КБ)

  • SQL Server: диспетчер памяти - общий объем памяти сервера (КБ)

  • SQL Server: диспетчер памяти - свободная память (КБ)

  • SQL Server: диспетчер памяти - кэш-память базы данных (КБ)

Что касается значения PLE, я использовал формулу Джоантана, и, к счастью, она сработала для меня.

Shanky
источник
6

Проблема здесь в том, что цифры не учитывают опыт конечного пользователя.

Отличный пример: у меня есть сервер базы данных, который используется для отслеживания каждого веб-сайта, который посещают сотрудники компании. Мне все равно, если он не справится со вставками во время пиковых нагрузок, потому что приложение переднего плана периодически пакетирует вставки, и медленные вставки не вызывают проблем у пользователей. Пользователи по-прежнему могут просматривать веб-страницы без медленных вставок.

В SELECT время отдел кадров просто запускает отчеты, когда их спрашивают о подозрительной истории просмотра для данного сотрудника, но им все равно, сколько времени занимают отчеты - они просто открывают отчет и уходят, чтобы заняться другими делами.

Производительность должна начинаться с вопроса: довольны ли пользователи производительностью? Если это так, оставьте систему там, где она есть.

Брент Озар
источник
Даже если вы используете больше памяти, чем нужно?
Джеймс Андерсон
2
Джеймс - вообще говоря, я не хочу вносить изменения, которые заставляют пользователей жаловаться. Если вы действительно хотите это сделать, вы можете постепенно уменьшить объем памяти для каждого сервера, пока пользователи не начнут жаловаться, но когда я уже перегружен работой, у меня обычно нет времени, чтобы предпринять эти шаги. Я должен сосредоточиться на задачах, которые сделают несчастных пользователей счастливыми, а не пытаться сделать счастливых пользователей несчастными. ;-)
Брент Озар
2
Хорошие моменты, Брент. Меня попросили проверить, не чрезмерно ли выделены некоторые серверы, потому что мы платим за память за ГБ в год. Во многих случаях, на которые я смотрю, объем оперативной памяти, по моему мнению, очень мал max server memory (mb), и поэтому я не очень хочу их уменьшать. Тем не менее, некоторые другие экземпляры имеют более 1000000 PLE, и, как таковые, являются довольно очевидными потенциальными кандидатами на падение оперативной памяти. Очевидно, что уменьшение ОЗУ приведет к увеличению числа операций ввода-вывода, и я не уверен, сколько это будет стоить.
Макс Вернон
1
Кроме того, смотреть на PLE по сравнению с max server memoryсеттингом - это нечто вроде курицы и яйца; чем ниже max server memoryнастройка, тем ниже будет минимальный «приемлемый» PLE, чтобы я мог застрять в постоянно понижающейся спирали. Я уверен, что, как вы упомянули, производительность пользователя в какой-то момент будет затронута.
Макс Вернон
Счетчики PLE следует избегать с 2012 года или когда у вас есть система NUMA, где каждый узел ведет себя как собственный маленький распределитель памяти. Если вы хотите, чтобы вы искали PLE для каждого узла NUMA, не завершенного, вы можете получить неверное значение
Shanky
3

Текущий T-SQL, который я использую для оценки PLE 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

Этот код сравнивает PLE с минимальным «приемлемым» PLE для количества max server memoryсконфигурированной системы. Если PLE заметно выше, чем допустимое число, это означает максимум на 10% ниже max server memory. Если значение PLE ниже допустимого значения PLE, оно предполагает максимум на 10% больше max server memory.

Если фактический объем выделенного пула буферов меньше, чем размер целевого пула буферов, рекомендуется уменьшить его max server memoryдо этого, плюс немного дополнительной памяти для потоков, отложенной записи и т. Д.

Код также просматривает различные счетчики производительности для таких вещей, как Lazy Writes / second, Free List Stalls и Batch Requests.

Код не идеален, я делюсь им здесь, чтобы получить информацию и для будущих пользователей SO.

Макс Вернон
источник
1
Максимум Имейте в виду, что начиная с SQL Server 2012 Buffer Pool целевые и зафиксированные значения не имеют смысла, и эти счетчики устарели. Вместо этого вы должны использовать Memory Manager Target Committed (KB) и текущий коммит. Если вы хотите узнать больше, почему он дает неправильное значение social.technet.microsoft.com/wiki/contents/articles/…
Шэнки