评估合理的缓冲池大小的确定方法是什么?


29

我试图提出一种理智的方法来了解max server memory (mb)设置是否合适(应该设置得更低还是更高,或者保持原样)。我知道max server memory (mb)应该始终足够低以为操作系统本身等留出空间。

我正在查看的环境有几百台服务器;我需要一个可靠的公式来确定缓冲池的当前大小是否合适,因为RAM是按分配给每个服务器的每GB的成本来计算的。整个环境已虚拟化,分配给VM的“物理” RAM可以轻松更改。

我现在正在查看一个特定的SQL Server实例,它的PLE为1,100,052秒,相当于12.7天(服务器已启动的时间)。该服务器的最大服务器内存设置为2560MB(2.5GB),其中实际仅提交了1380MB(1.3GB)。

我读过几个项目,包括一个由Jonathan Keheyias(职位),另一个由保罗·兰德尔(岗位),和其他几个人。Jonathan提倡监视每4GB缓冲池中的PLE在300以下,因为它太低了。对于上述SQL Server实例,300 * (2.5 / 4) = 187目标PLE确实非常低,低于300。此实例具有290GB的SQL Server数据(不包括日志文件),仅用于集成测试。假设在过去的12天代表该服务器的典型用法的,我想说的max server memory (mb)设置可能会降低。

在规模的另一端,我有另一台集成测试服务器,其PLE为294,其max server memory (mb)设置仅为1GB。该服务器仅具有224MB的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;否则,它将报告0。这可能是误导性的假阴性。

我是否还应该将PAGELATCHIO_*等待状态或其他等待类型视为内存压力的指标?

我的问题是,如何才能可靠地确定PLE和的“良好”目标值max server memory (mb)

Answers:


11

如您所知,没有通用的公式可以计算服务器的最大内存,您可以进行一些快速数学运算并得出一个值,但是最终您仍然需要Perfmon计数器的帮助来监视内存使用情况并进行相应的更改。我知道下面的一般公式,我也使用它。我从此链接中学到了这个公式

对于SQL Server 2005至2008 R2

请注意,从SQL Server 2005到2008 R2,最大服务器内存仅控制缓冲池。因此,最大服务器内存配置在此有点乏味,并且涉及很少的计算

  1. 立即为Windows OS保留2 G内存。

  2. 当然,系统会运行防病毒软件。请保留1.5G的防病毒功能。请注意,Mcafee和SQL Server不能并存,因此请确保为它留有足够的空间。您还可以检查perfmon计数器Perfmon Process-> Private bytes and Working Set以监视AV和运行在SQL Server上的其他小型应用程序的内存使用情况

在此处输入图片说明

  1. 考虑驱动程序/固件的内存需求。您必须根据系统上安装的驱动程序的内存需求来派生它。RAMMAP工具可以提供帮助

  2. 请考虑SQL Server的NonbPool(aka MTL或MTR)内存要求。

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

    +最多工作线程* 2MB

    +在大多数情况下,用于直接Windows分配的内存大约为0到300 MB,但是如果在SQL Server进程中加载​​了许多3方组件(包括链接服务器dll,第三方备份dll等),则可能必须增加内存。

    +如果您广泛使用CLR,则为CLR添加一些额外的内存。

  3. 考虑作业(包括复制代理,日志传送等)和将在服务器上运行的程序包的内存需求。根据正在运行的作业数量,它可以从MB到GB。对于中型服务器,您可以将其作为250 MB

  4. 确保有足够的可用空间用于操作系统。

    大约(每个GB到4G为止为100 MB)+(每个额外GB到12GB为止为50 MB)+(每个额外GB的25MB直到您的RAM大小)

  5. 其他内存要求。

    如果您的环境还有其他内存需求。

    服务器最大内存=总物理内存–(1 + 2 + 3 + 4 + 5 + 6 + 7)

    我没有包括SSIS.SSRS,SSAS的内存配置,您还需要从物理服务器总内存中减去这些服务所需的内存。

    完成上述配置后,您需要监视以下计数器

  • SQLServer:Buffer Manager-页面预期寿命(PLE):

  • SQLServer:缓冲区管理器-CheckpointPages /秒:

  • SQLServer:内存管理器-内存授权待定:

  • SQLServer:内存管理器-目标服务器内存:

  • SQLServer:内存管理器-服务器总内存

对于SQL Server 2012/2014。

From SQL Server 2012 onwards设置最大服务器内存变得容易。因为现在最大服务器内存几乎解决了所有内存消耗问题。服务器的最大内存控制SQL Server的内存分配,包括缓冲池,编译内存,所有缓存,qe内存授予,锁管理器内存和CLR内存(基本上是dm_os_memory_clerks中的任何“职员”)。线程堆栈,堆,SQL Server以外的链接服务器提供程序的内存或“非SQL Server” DLL分配的任何内存不受最大服务器内存的控制。

您可以将75-80%分配给SQL Server,然后使用perfmon计数器监视内存使用情况。在SQL Server 2012中,很少使用perfmon计数器。不建议使用缓冲区管理器计数器,必须使用内存管理器计数器

  • SQL Server:内存管理器-目标服务器内存(KB)

  • SQL Server:内存管理器-服务器总内存(KB)

  • SQL Server:内存管理器-可用内存(KB)

  • SQL Server:内存管理器-数据库缓存内存(KB)

关于PLE的价值,我使用了Joanthan的公式,幸运的是它对我有用。


6

这里的挑战是数字不考虑最终用户的体验。

很好的例子:我有一个数据库服务器用于跟踪公司员工访问的每个网站。我不在乎它是否不能在高峰负载期间跟上插入,因为前端应用程序会定期将插入批处理掉,并且缓慢的插入不会对用户造成问题。用户仍然可以浏览网页,而不会被缓慢的插入阻止。

在SELECT时,人力资源部门会在询问给定雇员的可疑浏览历史记录时才发出报告,但他们并不关心报告要花多长时间-他们只是打开报告并去做其他事情。

性能需要从一个问题开始:用户对性能满意吗?如果是这样,请将系统保留在原处。


即使您使用的内存超出了您的需要?
James Anderson

2
詹姆斯(James)-一般来说,我不想进行会引起用户抱怨的更改。如果您确实想这样做,则可以逐步降低每个服务器的内存量,直到用户开始抱怨为止,但是当我已经工作过度时,我通常没有时间采取这些步骤。我必须专注于使不满意的用户感到高兴的任务-而不是试图使不满意的用户感到不高兴。;-)
布伦特·欧扎

2
好点,布伦特。我被问到是否有些服务器超额配置,因为我们每年为每GB的内存付费。我正在查看的许多实例中的RAM数量很少max server memory (mb),因此,我非常不希望缩小它们的大小。但是,其他一些实例具有1,000,000 + PLE,因此很明显可能是RAM下降的潜在候选者。显然,降低RAM会造成的IOps的增加,我不知道成本是什么将是。
Max Vernon 2014年

1
另外,与max server memory设置相比看PLE 有点像鸡和蛋。在降低max server memory设置,降低最低“可接受” PLE是,这样我就可以在不断降低螺旋会被卡住。正如您所提到的,我敢肯定,用户性能有时会受到影响。
Max Vernon 2014年

从2012年开始,或者当您拥有NUMA系统(每个节点都充当自己的小内存分配器)时,您应该避免使用PLE计数器。如果您希望为每个未完成的NUMA节点查找PLE,则可能会得到不正确的值
Shanky 2014年

3

我用来评估PLE vs的当前T-SQL 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与max server memory已配置的系统数量的最小“可接受” PLE进行比较。如果PLE明显高于可接受的数字,则建议最大降低10%max server memory。如果PLE低于可接受的PLE,则建议最多增加10%max server memory

如果已提交的缓冲池的实际数量小于目标缓冲池的大小,则建议降低max server memory到该数量,再加上一些额外的内存用于线程,延迟写入等。

该代码还将查看各种性能计数器,以查找诸如“延迟写入/秒”,“空闲列表停顿”和“批处理请求”之类的内容。

该代码不是完美的,我在这里共享它以获得输入,并为将来的SO用户带来好处。


1
从SQL Server 2012缓冲池目标开始并已提交的Max Mind没有任何意义,并且不赞成使用这些计数器。相反,您必须使用“内存管理器目标已提交(KB)”和“当前已提交”。如果你想了解更多,为什么它给出错误的值social.technet.microsoft.com/wiki/contents/articles/...
Shanky
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.