Windows Server 2008 R2图元文件RAM使用情况


33

我有一台运行Windows Server 2008 R2 x64和4GB RAM的服务器,该服务器承载大约2-3百万个文件,其中大多数是图像文件。

在一个星期的时间里,我注意到服务器上的应用程序由于内存不足而导致对磁盘的过多分页,从而使爬网速度变慢,这对当前正在其上运行的所有服务产生连锁反应,性能问题。

在任务管理器中进行调查时,我注意到几乎所有4GB都在使用中,但是当您在“进程”选项卡中查看时,那里的所有内存使用量之和没有加起来,最多应该只有1.5GB。

使用Google查找解决方案时,似乎大多数RAM用于“元文件”中,该文件是文件系统上文件的NTFS信息缓存,因此系统不必再次向MFT查询信息。在任务管理器中,永远不会清除此缓存或将其标记为“缓存”,而在Sysinternal的RamMap中,则不会将其标记为“备用”。

有人建议安装KB979149修补程序,但是在尝试安装此修补程序时,它显示“此更新不适用于您的计算机”。

到目前为止,我发现的唯一临时修补程序是:

  1. 每1-3天从Sysinternals使用RAMmap到“空系统工作集”,这在任务管理器中将缓存标记为“备用”和“缓存”,以便其他应用程序可以使用该RAM。
  2. 重新启动计算机,这是不受欢迎的,因为此服务器正在服务公共网站。

目前,我必须每隔几天执行一次2.修复,以防止其达到瓶颈水平。

之前:(已使用800 MB RAM-其他应用程序无法使用此RAM)

在此处输入图片说明

之后:(800 MB RAM标记为缓存-可用于其他应用程序)

所以我对大家的问题是:是否存在限制该图元文件RAM使用的方法?


4
拥有2-3百万个文件的服务器上的4GB RAM是荒谬的。升级您的RAM或升级您的RAM。
pauska,2011年

1
没关系,Sysinternals的CacheSet允许我设置缓存大小,当前每隔一段时间运行一次,这样做已经为我解决了这个问题!
al2k4 2011年

6
添加RAM不能解决问题。元文件缓存也将填满它。我尝试在以4 GB开始并将其增加到12 GB的VMware guest虚拟机上执行此操作,并且发生了同样的事情。问题是该内存用于缓存,但根据Windows并未标记为缓存。它被错误地标记为活动/使用中内存,不幸的是,随着它的增长,它排挤了实际程序使用的真实活动/使用中内存,并开始分页到磁盘。当物理RAM填满时,一切都会变慢,您必须按照原始文章所述执行两种解决方案之一。

@ al2k4如果您的问题已解决,请单击正确答案旁边的对勾标记将其标记为已解决。欢迎来到服务器故障
迈克尔·汉普顿

Answers:


16

为解决这一问题,最好的方法是使用SetSystemFileCacheSizeAPI作为MS KB976618 责成 用于指示

不要定期清除缓存

使用该SetSystemFileCacheSize功能而不是定期清除缓存可以提高性能和稳定性。定期清除缓存将导致过多的图元文件和其他信息从内存中清除,Windows将必须将所需的信息从HDD重新读回RAM。每当您清除缓存时,这都会导致性能突然严重下降几秒钟,其后的良好性能会随着内存中填充图元文件数据而逐渐降低。

使用该SetSystemFileCacheSize函数设置的最小值和最大值将导致Windows将多余的旧图元文件数据标记为备用内存,常规缓存功能可以根据当前资源需求和常规缓存优先级使用或丢弃这些数据。如果Windows没有将内存用于其他任何事情,同时还保留了大量可用内存,这还允许将比您设置的活动内存最大值更多的元文件数据作为备用数据保存在内存中。这是始终保持系统性能特征的理想情况。

MS不支持第三方程序

如果您像我一样,并且不想在生产服务器上运行来自某个未知第三方的二进制文件,则需要一个正式的MS工具或一些可以在这些服务器上运行之前检查的代码。2008 R2的DynCache工具几乎不可能从M $中获得,而无需支付支持案例,并且坦率地说,基于2008年的代码,由于Windows已经具有动态调整大小所需的内置逻辑,因此该任务似乎过分膨胀缓存-它只需要知道适合您系统的最大值。

以上所有解决方案

我写了一个Powershell脚本,可在64位计算机上使用。您需要以具有提升的特权的管理员身份运行它。您应该能够按原样在任何x64 Windows Vista / Server 2008以及更高版本(包括10 / Server 2012 R2,包括任意RAM)上运行它。您不需要安装任何其他软件,因此可以使服务器/工作站完全受MS支持。

您应在每次引导时以提升的特权运行此脚本,以使设置永久生效。Windows Task Scheduler可以为您执行此操作。如果Windows安装在虚拟机内部,并且您更改了分配给该VM的RAM数量,则还应在更改后运行它。

您甚至可以在正在运行的系统上随时在运行的系统上运行此脚本,而不必重新引导系统或关闭任何服务。

# Filename: setfc.ps1
$version = 1.1

#########################
# Settings
#########################

# The percentage of physical ram that will be used for SetSystemFileCache Maximum
$MaxPercent = 12.5

#########################
# Init multipliers
#########################
$OSBits = ([System.IntPtr]::Size) * 8
switch ( $OSBits)
{
    32 { $KiB = [int]1024 }
    64 { $KiB = [long]1024 }
    default {
        # not 32 or 64 bit OS. what are you doing??
        $KiB = 1024 # and hope it works anyway
        write-output "You have a weird OS which is $OSBits bit. Having a go anyway."
    }
}
# These values "inherit" the data type from $KiB
$MiB = 1024 * $KiB
$GiB = 1024 * $MiB
$TiB = 1024 * $GiB
$PiB = 1024 * $TiB
$EiB = 1024 * $PiB


#########################
# Calculated Settings
#########################

# Note that because we are using signed integers instead of unsigned
# these values are "limited" to 2 GiB or 8 EiB for 32/64 bit OSes respectively

$PhysicalRam = 0
$PhysicalRam = [long](invoke-expression (((get-wmiobject -class "win32_physicalmemory").Capacity) -join '+'))
if ( -not $? ) {
    write-output "Trying another method of detecting amount of installed RAM."
 }
if ($PhysicalRam -eq 0) {
    $PhysicalRam = [long]((Get-WmiObject -Class Win32_ComputerSystem).TotalPhysicalMemory) # gives value a bit less than actual
}
if ($PhysicalRam -eq 0) {
    write-error "Cannot Detect Physical Ram Installed. Assuming 4 GiB."
    $PhysicalRam = 4 * $GiB
}
$NewMax = [long]($PhysicalRam * 0.01 * $MaxPercent)
# The default value
# $NewMax = 1 * $TiB


#########################
# constants
#########################

# Flags bits
$FILE_CACHE_MAX_HARD_ENABLE     = 1
$FILE_CACHE_MAX_HARD_DISABLE    = 2
$FILE_CACHE_MIN_HARD_ENABLE     = 4
$FILE_CACHE_MIN_HARD_DISABLE    = 8


################################
# C# code
# for interface to kernel32.dll
################################
$source = @"
using System;
using System.Runtime.InteropServices;

namespace MyTools
{
    public static class cache
    {
        [DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
        public static extern bool GetSystemFileCacheSize(
            ref IntPtr lpMinimumFileCacheSize,
            ref IntPtr lpMaximumFileCacheSize,
            ref IntPtr lpFlags
            );

        [DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
        public static extern bool SetSystemFileCacheSize(
          IntPtr MinimumFileCacheSize,
          IntPtr MaximumFileCacheSize,
          Int32 Flags
        );

        [DllImport("kernel32", CharSet = CharSet.Unicode)]
        public static extern int GetLastError();

        public static bool Get( ref IntPtr a, ref IntPtr c, ref IntPtr d )
        {
            IntPtr lpMinimumFileCacheSize = IntPtr.Zero;
            IntPtr lpMaximumFileCacheSize = IntPtr.Zero;
            IntPtr lpFlags = IntPtr.Zero;

            bool b = GetSystemFileCacheSize(ref lpMinimumFileCacheSize, ref lpMaximumFileCacheSize, ref lpFlags);

            a = lpMinimumFileCacheSize;
            c = lpMaximumFileCacheSize;
            d = lpFlags;
            return b;
        }


        public static bool Set( IntPtr MinimumFileCacheSize, IntPtr MaximumFileCacheSize, Int32 Flags )
        {
            bool b = SetSystemFileCacheSize( MinimumFileCacheSize, MaximumFileCacheSize, Flags );
            if ( !b ) {
                Console.Write("SetSystemFileCacheSize returned Error with GetLastError = ");
                Console.WriteLine( GetLastError() );
            }
            return b;
        }
    }

    public class AdjPriv
    {
        [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
        internal static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall, ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen);

        [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
        internal static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr phtok);

        [DllImport("advapi32.dll", SetLastError = true)]
        internal static extern bool LookupPrivilegeValue(string host, string name, ref long pluid);

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        internal struct TokPriv1Luid
        {
            public int Count;
            public long Luid;
            public int Attr;
        }
        internal const int SE_PRIVILEGE_ENABLED = 0x00000002;
        internal const int SE_PRIVILEGE_DISABLED = 0x00000000;
        internal const int TOKEN_QUERY = 0x00000008;
        internal const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;

        public static bool EnablePrivilege(long processHandle, string privilege, bool disable)
        {
            bool retVal;
            TokPriv1Luid tp;
            IntPtr hproc = new IntPtr(processHandle);
            IntPtr htok = IntPtr.Zero;
            retVal = OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok);
            tp.Count = 1;
            tp.Luid = 0;
            if(disable)
            {
                tp.Attr = SE_PRIVILEGE_DISABLED;
            } else {
                tp.Attr = SE_PRIVILEGE_ENABLED;
            }
            retVal = LookupPrivilegeValue(null, privilege, ref tp.Luid);
            retVal = AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero);
            return retVal;
        }
    }
}
"@
# Add the c# code to the powershell type definitions
Add-Type -TypeDefinition $source -Language CSharp

#########################
# Powershell Functions
#########################
function output-flags ($flags)
{
    Write-output ("FILE_CACHE_MAX_HARD_ENABLE  : " + (($flags -band $FILE_CACHE_MAX_HARD_ENABLE) -gt 0) )
    Write-output ("FILE_CACHE_MAX_HARD_DISABLE : " + (($flags -band $FILE_CACHE_MAX_HARD_DISABLE) -gt 0) )
    Write-output ("FILE_CACHE_MIN_HARD_ENABLE  : " + (($flags -band $FILE_CACHE_MIN_HARD_ENABLE) -gt 0) )
    Write-output ("FILE_CACHE_MIN_HARD_DISABLE : " + (($flags -band $FILE_CACHE_MIN_HARD_DISABLE) -gt 0) )
    write-output ""
}

#########################
# Main program
#########################

write-output ""

#########################
# Get and set privilege info
$ProcessId = $pid
$processHandle = (Get-Process -id $ProcessId).Handle
$Privilege = "SeIncreaseQuotaPrivilege"
$Disable = $false
Write-output ("Enabling SE_INCREASE_QUOTA_NAME status: " + [MyTools.AdjPriv]::EnablePrivilege($processHandle, $Privilege, $Disable) )

write-output ("Program has elevated privledges: " + ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator") )
write-output ""
whoami /PRIV | findstr /I "SeIncreaseQuotaPrivilege" | findstr /I "Enabled"
if ( -not $? )  {
    write-error "user Security Token SE_INCREASE_QUOTA_NAME: Disabled`r`n"
}
write-output "`r`n"


#########################
# Get Current Settings
# Init variables
$SFCMin = 0
$SFCMax = 0
$SFCFlags = 0
#Get Current values from kernel
$status = [MyTools.cache]::Get( [ref]$SFCMin, [ref]$SFCMax, [ref]$SFCFlags )
#typecast values so we can do some math with them
$SFCMin = [long]$SFCMin
$SFCMax = [long]$SFCMax
$SFCFlags = [long]$SFCFlags
write-output "Return values from GetSystemFileCacheSize are: "
write-output "Function Result : $status"
write-output "            Min : $SFCMin"
write-output ("            Max : $SFCMax ( " + $SFCMax / 1024 / 1024 / 1024 + " GiB )")
write-output "          Flags : $SFCFlags"
output-flags $SFCFlags


#########################
# Output our intentions
write-output ("Physical Memory Detected : $PhysicalRam ( " + $PhysicalRam / $GiB + " GiB )")
write-output ("Setting Max to " + $MaxPercent + "% : $NewMax ( " + $NewMax / $MiB + " MiB )`r`n")

#########################
# Set new settings
$SFCFlags = $SFCFlags -bor $FILE_CACHE_MAX_HARD_ENABLE # set max enabled
$SFCFlags = $SFCFlags -band (-bnot $FILE_CACHE_MAX_HARD_DISABLE) # unset max dissabled if set
# or if you want to override this calculated value
# $SFCFlags = 0
$status = [MyTools.cache]::Set( $SFCMin, $NewMax, $SFCFlags ) # calls the c# routine that makes the kernel API call
write-output "Set function returned: $status`r`n"
# if it was successfull the new SystemFileCache maximum will be NewMax
if ( $status ) {
    $SFCMax = $NewMax
}


#########################
# After setting the new values, get them back from the system to confirm
# Re-Init variables
$SFCMin = 0
$SFCMax = 0
$SFCFlags = 0
#Get Current values from kernel
$status = [MyTools.cache]::Get( [ref]$SFCMin, [ref]$SFCMax, [ref]$SFCFlags )
#typecast values so we can do some math with them
$SFCMin = [long]$SFCMin
$SFCMax = [long]$SFCMax
$SFCFlags = [long]$SFCFlags
write-output "Return values from GetSystemFileCacheSize are: "
write-output "Function Result : $status"
write-output "            Min : $SFCMin"
write-output ("            Max : $SFCMax ( " + $SFCMax / 1024 / 1024 / 1024 + " GiB )")
write-output "          Flags : $SFCFlags"
output-flags $SFCFlags

顶部附近有一行显示,$MaxPercent = 12.5它将新的最大工作集(活动内存)设置为总物理RAM的12.5%。Windows将根据系统需求动态调整活动内存中图元文件数据的大小,因此您无需动态调整此最大值。

这不会解决因映射文件缓存太大而引起的任何问题。

我还制作了一个GetSystemFileCacheSizePowershell脚本并将其发布在StackOverflow上


编辑:我还应该指出,您不应从同一Powershell实例中多次运行这两个脚本中的任何一个,否则您将收到错误消息,Add-Type即已进行了调用。

编辑:将SetSystemFileCacheSize脚本更新为1.1版,该脚本可以为您计算适当的最大缓存值,并具有更好的状态输出布局。

编辑:现在我已经升级了Windows 7笔记本电脑,我可以告诉您该脚本可以在Windows 10中成功运行,尽管我尚未测试是否仍然需要它。但是即使移动虚拟机HDD文件,我的系统仍然稳定。


DynCache工具可从microsoft.com/en-us/download/details.aspx?id=9258免费下载,并支持2008 R2。
Jakub Berezanski 2014年

就是现在。R2 Windows版本和DynCache版本之间的时间间隔很长。有关ms博客帖子的更新,请参见blogs.technet.com/b/yongrhee/archive/2010/02/16/…。我仍然更喜欢我的解决方案,因为它不需要额外的资源即可运行另一个服务。我的脚本使我们的服务器变得非常稳定,因此我不会将其更改为DynCache。
BeowulfNode42 2014年

@ BeowulfNode42-我们遇到了映射文件缓存变大的问题。关于如何解决这个问题,您有什么建议吗?我当时假设设置系统文件缓存大小也可以解决该问题?你有,如果任何想法(浮肿) DynCache工具将解决这个问题?
Lieven Keersmaekers

fwiw-我刚刚在测试服务器上尝试过,映射文件(rammap)从12GB Active,0GB Standby变为8GB Active,4GB Standby。出于所有意图和目的,这似乎也适用于“映射文件”吗?
Lieven Keersmaekers,2016年

@LievenKeersmaekers,这很奇怪。也许这是某种影响。尽管我还没有尝试过DynCache工具,但我还没有找到解决从快速存储复制文件到慢速文件时遇到的映射文件缓存问题的好方法,因为对我们而言,映射文件缓存问题主要只是一个令人讨厌的临时问题。降低管理备份的服务器的速度。供将来参考,该测试系统有多少内存,您是否按照12.5%的设置运行脚本,并且是否还记得或记录了其他类型的内存大小?
BeowulfNode42 '16

4

我并没有声称自己是Windows OS中内存或磁盘缓存内部工作的专家,但是我有两个发现:

  1. 如果操作系统不将数据缓存在内存中,则必须从磁盘读取数据,这是存储介质的指数级增长,其速度要比内存慢得多,因此您现在看到的性能问题几乎肯定会更糟。

  2. 您正在尝试通过解决问题的症状而不是问题的原因来解决问题。问题的原因几乎可以肯定是缺少足够的物理RAM,我的建议是解决这个问题。

另外,虽然缓存可能使用1.5GB的RAM,但我想知道其他进程和服务的内存使用量是多少,解决方案可能是调查这种使用是否存在潜在问题。


究竟。如果OP减少了图元文件的RAM使用量,则系统将不得不从磁盘加载更多的元数据,因为更少的元数据将存储在内存中,从而使情况变得更糟。
大卫·史瓦兹

1
感谢您的反馈。几乎没有什么东西,该服务器主要是带有MySQL数据库的Web服务器,并且不经常读取文件,因此,不在缓存中的元数据的轻微影响很小,清除后性能会大大提高。随着时间的推移,它读取的不同文件的数量就是缓存大小越来越大的原因。我很清楚,更多的RAM可以解决它,但是当服务器上的应用程序或脚本确实需要它以避免分页时,“缓存”的想法不是要释放内存吗?为什么将某些特定的缓存始终标记为活动,这使我感到困惑。
al2k4 2011年

您显然自己从未经历过此问题。许多拥有32、64和128 GB RAM的人都会遇到此问题,因为图元文件数据占用了太多的RAM,Windows并没有将其释放,因为它被标记为活动内存,而不是备用(aka缓存)内存。如我在回答中所述,使用SetSystemFileCacheSize API会强制Windows将许多图元文件数据标记为备用内存,然后缓存管理系统便能够确定RAM中保留的内容和丢弃的内容的优先级。
BeowulfNode42 2014年

巨魔?这个问题已经有两年多了。
joeqwerty 2014年

@joeqwerty我一直在网上看到有关此问题的新帖子。许多相关的搜索都得出了这个问题。因为我正在更新自己的答案,并且我相信您的答案“无用”,所以我将其标记为此类并评论原因。如果那使我成为巨魔,那就这样吧。
BeowulfNode42 2014年

3

对于那些仅添加更多RAM却给出了明显但无效的解决方案的人们来说,您显然没有直接解决此问题。

如先前的海报所述,您在问题上投入多少内存都无所谓...它们都将被填满。我正在应用服务器上运行Atlassian工具集,该工具集已从32位(2003)迁移到64位(2008)。显而易见,性能下降了。

当查看任务管理器时,几乎所有的内存都被用完了。即使正在运行的进程没有反映这一点。当我们将内存从8 GB增加到16 GB时,问题也消耗了额外的内存。

解决该问题的唯一方法是重新启动服务器,这降低了与进程相等的内存使用量(约3.5 GB)。大约一天后,这又开始爬升。

我知道这是一个新的Microsoft错误/功能,很高兴找到这篇文章。我喜欢Microsoft如何将所有重要细节留给用户弄清楚。我下载了RamMap,您会认为它是本机实用程序,现在我可以看到图元文件的用法。我们会将缓存设置为每隔几天清除一次,希望这可以解决该问题。

有趣的是,我仅在几台已迁移的服务器上看到了此问题,所以我想知道图元文件是否仅由某些类型的应用程序提供。


1
以我的经验,图元文件的内存使用量不会超出文件系统元数据的大小(毕竟这就是缓存),因此至少在某些情况下,升级RAM以允许文件系统元数据适合内存是可行的解决方案。我还建议客户端通过删除残骸,例如数月以来未触及的数百万个临时文件,来减少文件系统元数据的大小。Windows有效地选择了内存中的NTFS元数据而不是应用程序内存,这很尴尬,但是在没有MFT的情况下查找文件的群集可能会非常慢。
詹姆斯·L

2
我同意-添加更多的ram不能解决问题,只会消耗更多的内存,所有其他进程最终都会停顿下来。我最近升级到24 GB,仅使SQL占用8(精简),并且在图元文件中有12。.James N-您使用哪个工具定期清除它?
sirthomas

2

使用SysInternals CacheSet工具可以快速免费解决此问题。只需将最大工作集设置为小于系统RAM数量的合适值,然后应用。


1

抱歉,这么直接,但是您如何将服务器升级到比目前的工作站数量更高的内存?16GB内存价格便宜。比您花费半天的时间还便宜。


2
这将永远解决该问题,但我们的服务器由第三方远程托管。我们的主机将收取大量费用,只是为了每月增加RAM。因此,我们尽可能避免这种情况。
al2k4

2
是的 猜猜是什么;)这就是为什么我购买我的硬件。主机很疯狂-您可以在3个月内购买RAM。好了,要学习的课程:不专业的设置会再次吸引您。
TomTom,

所有64位Windows系统都将1TB设置为活动内存中具有的最大图元文件数据量(当其他事物需要更多内存时,不视为准备使用的缓存备用内存)。我不了解您,但还没有看到有这么大内存的Windows框。您不必安装更多的RAM即可利用HDD上存储的更多文件。NTFS每个卷最多应支持4,294,967,295个文件。这样,单个4TB NTFS驱动器应能够支持超过9亿个文件。尝试在其上运行碎片整理或备份,它将失败或爬行。
BeowulfNode42 2014年

1
是的,如果您的“服务器”的内存比功能强大的笔记本电脑少,则应该知道。这不是关于“高得离谱”。它是关于“获取足够的服务器名称”。
TomTom 2014年

1

这是下载Microsoft DynCache工具的链接-无需创建票证或付款。 http://www.microsoft.com/zh-CN/download/details.aspx?displaylang=en&id=9258

(抱歉,现在仅注意这不适用于R2版本)

有关持续缓存增长的已知问题,请参见 Microsoft博客:http : //blogs.msdn.com/b/ntdebugging/archive/2007/11/27/too-much-cache.aspx

Windows Server 2008 R2的[更新]工作修补程序。

我在Codeplex上找到了示例C#代码,并使用Visual Studio迅速创建了一个控制台C#项目并进行了编译,工作。

https://asstoredprocedures.svn.codeplex.com/svn/ASSP/FileSystemCache.cs

请注意,您需要添加对Microsoft.AnalysisServices.AdomdClient的引用,可以在这里找到:

C:\ Program档案(x86)\ Microsoft.NET \ ADOMD.NET

并用不需要的对XMLaDiscover的引用注释掉ClearAllCaches()方法。将此放入TaskScheduler。


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.