Obecny T-SQL, którego używam do oceny PLE vs max server memory
to:
/*
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 memory
skonfigurowanej 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 memory
do 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.