web-dev-qa-db-fra.com

Métafichier Windows Server 2008 R2 RAM Utilisation

J'ai un serveur qui exécute Windows Server 2008 R2 x64 avec 4 Go de RAM qui héberge environ 2-3 millions de fichiers, dont la majorité sont des fichiers image.

Au cours d'une semaine, j'ai remarqué que les applications sur le serveur ralentissaient à une analyse en raison d'une pagination excessive sur le disque en raison d'une mémoire insuffisante, ce qui a un effet d'entraînement sur tous les services en cours d'exécution, provoquant un problème majeur problème de performance.

Après enquête dans le Gestionnaire des tâches, j'ai remarqué que presque tous les 4 Go étaient en cours d'utilisation, mais lorsque vous regardez dans l'onglet Processus, la somme de toute l'utilisation de la mémoire n'y correspond pas et au plus seulement 1,5 Go est censé être utilisé.

En utilisant Google pour trouver une solution, il semble que la plupart des RAM ont été utilisés dans le "métafichier" qui est un cache d'informations NTFS pour les fichiers du système de fichiers afin que le système ne dispose pas de pour interroger à nouveau le MFT. Ce cache n'est jamais effacé ou marqué comme "cache" dans le Gestionnaire des tâches ou comme "Veille" dans RamMap de Sysinternal.

Il y avait ne suggestion pour installer le correctif KB979149 mais en essayant de l'installer, il est dit "Cette mise à jour n'est pas applicable à votre ordinateur".

Les seules corrections temporaires que j'ai trouvées jusqu'à présent sont:

  1. Utilisez RAMmap de Sysinternals à "Empty System Working Set" tous les 1 à 3 jours, ce qui marque le cache comme "veille" et "cache" dans le Gestionnaire des tâches afin que le RAM puisse être utilisé par d'autres applications).
  2. Redémarrez la machine, ce qui n'est pas souhaitable car ce serveur dessert des sites Web publics.

Pour le moment, je dois effectuer le correctif tous les deux jours pour l'empêcher d'atteindre des niveaux de goulot d'étranglement.

Avant: (800 Mo RAM utilisée - les autres applications ne peuvent pas utiliser cette RAM))

enter image description here

Après: (800 Mo RAM marqué comme cache - disponible pour d'autres applications)

Donc, ma question est la suivante: existe-t-il une méthode pour limiter l'utilisation de RAM de ce métafichier?

33
al2k4

La meilleure méthode pour résoudre ce problème consiste à utiliser l'API SetSystemFileCacheSize as MS KB976618 instruit utilisé pour instruire .

Ne pas vider périodiquement le cache

L'utilisation de la fonction SetSystemFileCacheSize plutôt que l'effacement du cache améliore périodiquement les performances et la stabilité. Vider le cache périodiquement entraînera la suppression de trop de métafichier et d'autres informations de la mémoire, et Windows devra relire les informations requises dans RAM du disque dur. Cela crée une soudaine et grave baisse des performances pendant plusieurs secondes chaque fois que vous videz le cache, suivie de bonnes performances qui se dégradent lentement à mesure que la mémoire se remplit de données de métafichier.

L'utilisation de la fonction SetSystemFileCacheSize définit des valeurs minimales et maximales qui entraîneront Windows signalant les anciennes données de métafichier excédentaires en tant que mémoire de secours que les fonctions de mise en cache normales peuvent utiliser ou supprimer en fonction des demandes de ressources actuelles et des priorités de cache normales. Cela permet également à plus de données de métafichier que la mémoire active maximale que vous définissez, d'être en mémoire en tant que données de secours si Windows n'utilise pas la mémoire pour autre chose, tout en conservant une grande quantité de mémoire disponible. Il s'agit de la situation idéale pour maintenir en permanence les caractéristiques de performance du système.

Les programmes tiers ne sont pas pris en charge par MS

Si vous êtes comme moi et que vous ne voulez pas exécuter un binaire d'un tiers inconnu sur vos serveurs de production, vous voulez un outil MS officiel ou du code que vous pouvez inspecter avant de l'exécuter sur ces serveurs. L'outil DynCache pour 2008 R2 est pratiquement impossible à obtenir à partir de M $ sans payer pour un cas de support et très franchement, sur la base du code pour 2008, il semble trop gonflé pour la tâche car Windows a déjà la logique intégrée nécessaire pour dimensionner dynamiquement le cache — il lui suffit de connaître un maximum approprié pour votre système.

Solution à tout ce qui précède

J'ai écrit un script Powershell qui fonctionne sur des machines 64 bits. Vous devez l'exécuter en tant qu'administrateur avec des privilèges élevés. Vous devriez pouvoir l'exécuter, tel quel, sur n'importe quel Windows x64 Vista/Server 2008 jusqu'à 10/Server 2012 R2 inclus, avec n'importe quelle quantité de RAM. Vous n'avez pas besoin d'installer de logiciel supplémentaire et, par conséquent, gardez votre serveur/poste de travail entièrement pris en charge par MS.

Vous devez exécuter ce script à chaque démarrage avec des privilèges élevés pour que le paramètre soit permanent. Le Planificateur de tâches Windows peut le faire pour vous. Si l'installation de Windows est à l'intérieur d'une machine virtuelle et que vous modifiez la quantité de RAM allouée à cette machine virtuelle, vous devez également l'exécuter après la modification.

Vous pouvez exécuter ce script à tout moment sur un système en cours d'exécution, même en cours d'utilisation, sans avoir à redémarrer le système ou à arrêter des services.

# 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

Il y a une ligne en haut qui dit $MaxPercent = 12.5 qui définit le nouvel ensemble de travail maximal (mémoire active) à 12,5% de la RAM physique totale. Windows dimensionnera dynamiquement la quantité de données de métafichier dans la mémoire active en fonction des demandes du système, vous n'avez donc pas besoin d'ajuster dynamiquement ce maximum.

Cela ne résoudra pas les problèmes que vous rencontrez avec le cache de fichiers mappés trop volumineux.

J'ai également créé un script Powershell GetSystemFileCacheSize et l'a publié sur StackOverflow .


Edit: je dois également souligner que vous ne devez pas exécuter l'un de ces 2 scripts à partir de la même instance Powershell plus d'une fois, ou vous recevrez l'erreur que le Add-Type l'appel a déjà été effectué.

Edit: script SetSystemFileCacheSize mis à jour vers la version 1.1 qui calcule une valeur de cache maximale appropriée pour vous et a une présentation de sortie d'état plus agréable.

Edit: Maintenant que j'ai mis à niveau mon ordinateur portable Windows 7, je peux vous dire que le script s'exécute correctement dans Windows 10, même si je n'ai pas testé s'il est toujours nécessaire. Mais mon système est toujours stable même lors du déplacement de fichiers HDD de machine virtuelle.

16
BeowulfNode42

Je ne prétends pas être un expert en ce qui concerne le fonctionnement interne de la mémoire ou de la mise en cache de disque dans un système d'exploitation Windows, mais j'ai deux observations:

  1. Si le système d'exploitation ne mettait pas en cache les données en mémoire, il devrait les lire sur le disque, qui est un support de stockage exponentiellement plus lent que la mémoire, donc le problème de performances que vous voyez maintenant serait presque certainement pire.

  2. Vous essayez de résoudre le problème en traitant un symptôme du problème au lieu de la cause du problème. La cause du problème est presque certainement un manque de physique suffisant RAM et ma suggestion serait de remédier à cela.

En outre, bien que le cache utilise 1,5 Go de RAM, je me demande quelle est l'utilisation de la mémoire pour d'autres processus et services et la solution pourrait être d'étudier cette utilisation pour des problèmes potentiels.

4
joeqwerty

Aux personnes qui ont donné la solution évidente mais inefficace de simplement ajouter plus de RAM, vous n'avez clairement pas traité ce problème de première main.

Comme indiqué par une affiche précédente, peu importe combien RAM vous jetez sur le problème ... tout se remplira. J'utilise un ensemble d'outils Atlassian sur notre serveur d'application qui a été migré de 32 bits (2003) à 64 bits (2008). Il est immédiatement apparu qu'il y avait une perte de performances.

En regardant le gestionnaire de tâches, presque toute la mémoire a été utilisée; même si les processus en cours d'exécution ne le reflètent pas. Lorsque nous avons augmenté la mémoire de 8 Go à 16 Go, le problème a également consommé la mémoire supplémentaire.

La seule façon de traiter le problème était de redémarrer le serveur, ce qui a réduit l'utilisation de la mémoire égale aux processus (environ 3,5 Go). Cela a recommencé à grimper dans un jour ou deux.

Je savais que c'était un nouveau bug/fonctionnalité de Microsoft et j'étais heureux de trouver cet article. J'adore la façon dont Microsoft laisse ce détail très important pour les utilisateurs à comprendre. J'ai téléchargé RamMap, que vous penseriez être un utilitaire natif, et maintenant je peux voir l'utilisation du métafichier. Nous allons configurer le cache pour qu'il soit effacé tous les quelques jours et nous espérons que cela résoudra le problème.

Il est intéressant de noter que je n'ai vu ce problème que sur un de plusieurs de nos serveurs migrés, donc je me demande si le métafichier n'est alimenté qu'à partir de certains types d'applications.

3
James N.

Ce problème peut être résolu rapidement et gratuitement à l'aide de l'outil SysInternals CacheSet. Réglez simplement le jeu de travail maximum sur une valeur appropriée inférieure à la quantité de RAM système et appliquez.

2
OneSpeed

Voici un lien pour télécharger l'outil Microsoft DynCache - pas besoin de créer un ticket ou de payer. http://www.Microsoft.com/en-us/download/details.aspx?displaylang=en&id=9258

(excuses - je remarque seulement maintenant que ce n'est pas pour la version R2)

Le problème connu pour la croissance continue du cache est décrit ici sur le blog Microsoft: http://blogs.msdn.com/b/ntdebugging/archive/2007/11/27/too -much-cache.aspx

[mise à jour] correctif de travail pour Windows Server 2008 R2.

J'ai trouvé un exemple de code C # sur Codeplex, rapidement créé un projet de console C # avec Visual Studio et compilé, travaillé.

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

Remarque, vous devrez ajouter une référence à Microsoft.AnalysisServices.AdomdClient qui se trouve ici:

C:\Program Files (x86)\Microsoft.NET\ADOMD.NET

et commentez la méthode ClearAllCaches () avec (dans mon cas) des références inutiles à XMLaDiscover. Jetez cela dans TaskScheduler.

1
sirthomas

Désolé d'être si direct, mais qu'en est-il de la mise à niveau du serveur vers une quantité de RAM un peu plus élevée que celle des stations de travail de nos jours? 16 Go de mémoire sont vraiment bon marché. Moins cher que même une demi-journée de votre temps.

1
TomTom

Vous pouvez obtenir l'outil DynCache de MS qui permettra de restreindre l'utilisation de RAM par métafichier.

Cliquez ici pour obtenir l'outil de MS .

0
cakiran