Поточний T-SQL, який я використовую для оцінки PLE vs 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
до цієї кількості, плюс додаткову пам'ять для потоків, ліниві записи тощо.
Код також розглядає різні лічильники продуктивності для таких речей, як «Ледачі записи / секунди», «Безкоштовні списки списку» та «Пакетні запити».
Код не є ідеальним, я ділюсь ним тут, щоб отримати інформацію та на користь майбутніх користувачів ПП.