SQL Server Memory Pressure
Speicherdruck erkennen, Richtung bestimmen, Ursache beheben – bevor jemand reflexhaft RAM bestellt.
Memory Pressure — wenn der Buffer Pool zu klein wird
Page Life Expectancy im Sturzflug, Grants in der Warteschlange: So erkennst du, ob der Druck von innen oder von außen kommt.
Es ist Dienstagvormittag, 09:12 Uhr. Bei der Trendforge Digital GmbH ist seit letzter Woche alles ein bisschen zäher — nicht dramatisch, kein Ausfall, nur dieses zermürbende Gefühl, dass jede Maske eine Sekunde länger braucht. Das Monitoring zeigt nichts Rotes, die CPU langweilt sich, das Storage-Team meldet „alles grün“. Erst der Blick in den Perfmon bringt die Spur: Die Page Life Expectancy, monatelang stabil im fünfstelligen Bereich, sägt seit genau sieben Tagen im Minutentakt zwischen 12.000 und 200. Sieben Tage — exakt seit dem Wartungsfenster, in dem das Virtualisierungs-Team der VM „ungenutzten Arbeitsspeicher zurückgeholt“ hat. 32 GB weniger, und niemand hat es dem DBA gesagt.
Willkommen bei der Memory Pressure — dem Engpass, der sich nie als Knall zeigt, sondern als schleichende Zähigkeit, und der gern von außen kommt, ohne dass am SQL Server irgendjemand etwas geändert hat. Dieser Beitrag aus dem SQL-Server-Notarztwagen (die Triage-Übersicht liegt unter /sql-server-notarztwagen/) zeigt dir, wie du Speicherdruck zweifelsfrei nachweist, die Richtung bestimmst — von außen oder von innen? — und die fünf häufigsten Ursachen gezielt abstellst, statt reflexhaft RAM zu bestellen.
Was bedeutet Memory Pressure technisch?
Der SQL Server ist ein notorischer Speicher-Sammler, und das mit Absicht: Jede Datenseite, die einmal von der Platte gelesen wurde, bleibt im Buffer Pool liegen — denn ein Lesezugriff aus dem RAM ist um Größenordnungen billiger als einer von der Platte. Dazu kommen die anderen Mieter im Speicherhaushalt: der Plan-Cache mit den kompilierten Ausführungsplänen, der Workspace-Speicher für die Memory Grants laufender Abfragen und ein Rudel kleinerer Memory Clerks für Sperren, CLR, In-Memory OLTP und Co. Das Budget für all das setzt Max Server Memory — und Memory Pressure heißt schlicht: Das Budget reicht für die Arbeitslast nicht mehr, und die Mieter beginnen, sich gegenseitig zu verdrängen.
Entscheidend für die Therapie ist die Richtung des Drucks. Externer Druck kommt von außerhalb des SQL Servers: Das Betriebssystem braucht Speicher (weil Max Server Memory zu großzügig gesetzt ist oder ein anderer Prozess wuchert) — oder, der moderne Klassiker, der VM-Host zieht dem Gast per Ballooning Speicher ab. Der SQL Server gibt dann brav Speicher zurück und schrumpft seinen Buffer Pool, ganz gleich, was konfiguriert ist. Interner Druck entsteht innerhalb des Budgets: Die Workload ist gewachsen, Scans fluten den Cache, übergroße Memory Grants reservieren Unmengen Workspace, oder ein einzelner Clerk bläht sich auf. Beide Richtungen erzeugen dieselben Symptome — und genau deshalb ist die Richtungsbestimmung Diagnose-Schritt eins.
Die Symptome selbst: eine absackende Page Life Expectancy (Seiten werden verdrängt, kaum dass sie geladen sind), als Folge davon steigende PAGEIOLATCH-Waits (der Server liest dieselben Daten immer wieder nach) — und auf der zweiten Front RESOURCE_SEMAPHORE-Wartezeiten, wenn Abfragen auf ihre Speicherzuteilung warten müssen, bevor sie überhaupt starten dürfen. Wer in der Diagnose-Pyramide des „Server langsam“-Beitrags bei diesen Befunden gelandet ist, ist hier richtig abgebogen.
|
Abgrenzung: externer vs. interner Druck Externer Druck — das OS oder der VM-Host nimmt dem SQL Server Speicher weg. Erkennungszeichen: Der Speicher des SQL-Prozesses schrumpft sichtbar, im Systemlog tauchen Meldungen wie „a significant part of sql server process memory has been paged out“ auf. Therapie außerhalb von SQL: Budget, Ballooning, Mitbewohner. |
|---|
Der Diagnose-Pfad: die Speicher-Landkarte
Die folgende Skizze zeigt die Bewohner des Speichers und die beiden Druckrichtungen — sie ist die Landkarte für die Skripte im nächsten Abschnitt:

Erste Diagnose: konkrete Skripte
Schritt 1 — Die Kennzahlen: PLE, Grants, Gesamtlage
|
— Memory-Kennzahlen auf einen Blick SELECT counter_name, instance_name, cntr_value FROM sys.dm_os_performance_counters WHERE (object_name LIKE N'%Buffer Node%' AND counter_name = N'Page life expectancy') — PLE pro NUMA-Knoten! OR (object_name LIKE N'%Memory Manager%' AND counter_name IN ( N'Memory Grants Pending', — > 0 = Abfragen warten auf Speicher N'Memory Grants Outstanding', N'Total Server Memory (KB)', N'Target Server Memory (KB)')) ORDER BY object_name, counter_name; |
|---|
Drei Lesarten: Erstens, die PLE pro NUMA-Knoten — auf Mehrknoten-Maschinen kann ein einzelner Knoten leiden, während der Durchschnitt unauffällig aussieht. Zweitens, Memory Grants Pending: Jeder Wert über null heißt, dass gerade Abfragen in der Warteschlange stehen — das ist akuter interner Druck. Drittens, das Verhältnis Total zu Target Server Memory: Liegt Total dauerhaft unter Target, bekommt der SQL Server seinen Wunschspeicher nicht — ein starkes Indiz für externen Druck, gerade in VMs. Den endgültigen Beweis für ausgelagerten SQL-Speicher liefert das Windows-Systemlog mit der berüchtigten „paged out“-Meldung.
Schritt 2 — Die Clerks: Wer hält den Speicher?
|
— Top-10-Speicherverbraucher (Memory Clerks) SELECT TOP (10) [type] AS Clerk, SUM(pages_kb) / 1024 AS BelegtMB FROM sys.dm_os_memory_clerks GROUP BY [type] ORDER BY SUM(pages_kb) DESC; |
|---|
Das gesunde Bild: MEMORYCLERK_SQLBUFFERPOOL thront mit weitem Abstand oben, der Plan-Cache (CACHESTORE_SQLCP, CACHESTORE_OBJCP) folgt in moderater Größe. Verdächtig ist jeder andere Clerk, der in Buffer-Pool-Nähe wächst — ein aufgeblähter Ad-hoc-Plan-Cache etwa frisst den Datencache von innen auf, dazu mehr bei Ursache fünf.
Schritt 3 — Die Grants: Wer wartet, wer prasst?
|
— Memory Grants: Wartende und Grossverbraucher SELECT mg.session_id, mg.requested_memory_kb / 1024 AS AngefordertMB, mg.granted_memory_kb / 1024 AS BewilligtMB, — NULL = wartet! mg.wait_time_ms / 1000 AS WaitSec, t.text AS StatementText FROM sys.dm_exec_query_memory_grants AS mg OUTER APPLY sys.dm_exec_sql_text(mg.sql_handle) AS t ORDER BY mg.requested_memory_kb DESC; |
|---|
Zeilen mit BewilligtMB = NULL sind die Wartenden — und ganz oben in der Liste stehen die Prasser: Abfragen, die absurde Mengen anfordern. Ein Report, der 20 GB reserviert und 200 MB nutzt, blockiert den Topf für alle anderen. Solche Anforderungs-Größen entstehen fast immer aus schiefen Kardinalitätsschätzungen — die Spur führt zu Ursache vier.
Die häufigsten Ursachen — und was du dagegen tust
1. Max Server Memory falsch gesetzt — in beide Richtungen
Der Klassiker in zwei Geschmacksrichtungen. Variante eins: Der Wert steht noch auf dem Default (praktisch unbegrenzt) — der SQL Server frisst die Maschine leer, das OS beginnt zu paging, und im schlimmsten Fall landet der Buffer Pool selbst in der Auslagerungsdatei: externer Druck, hausgemacht. Variante zwei: Der Wert wurde vor Jahren auf einer kleineren Maschine gesetzt und nie angepasst — der Server hat 128 GB, darf aber nur 32 nutzen. So setzt du das Budget richtig:
|
— Max Server Memory setzen (Beispiel: 64-GB-Maschine, dedizierter SQL) — Faustregel: 4 GB + ~1 GB je 8 GB RAM fuers OS reservieren EXEC sp_configure 'show advanced options', 1; RECONFIGURE; EXEC sp_configure 'max server memory (MB)', 57344; — 56 GB fuer SQL RECONFIGURE;
— Kontrolle: aktueller Wert SELECT value_in_use FROM sys.configurations WHERE name = N'max server memory (MB)'; |
|---|
Die Faustregel ist ein Startwert, kein Naturgesetz: Läuft auf der Maschine mehr als der reine Datenbankdienst — SSIS-Pakete, Reporting Services, ein gefräßiger Virenscanner —, braucht das OS-Polster entsprechend mehr. Nach der Änderung beobachten: verfügbarer OS-Speicher stabil im Plus, keine „paged out“-Meldungen.
2. Der VM-Host nimmt sich Speicher zurück
Die Lead-Szene: In virtualisierten Umgebungen ist der konfigurierte RAM der VM eine Obergrenze, kein Versprechen. Ist der Host überbucht, holt er sich per Ballooning Speicher aus den Gästen zurück — und der SQL Server, der „seinen“ Buffer Pool für gesichert hielt, muss ihn hergeben. Erkennungszeichen: Total Server Memory bricht ein, ohne dass am Gast jemand etwas geändert hat. Die Therapie ist organisatorisch und technisch: Mit dem Virtualisierungs-Team eine Speicher-Reservierung für die SQL-VM vereinbaren (für Produktions-Datenbanken Standard, nicht Luxus) — und als Sicherheitsgurt Lock Pages in Memory für das Dienstkonto, damit der Buffer Pool wenigstens nicht in die Auslagerungsdatei wandert. Beides zusammen, nicht eines von beiden.
3. Die Workload ist dem Cache entwachsen — oder Scans fluten ihn
Der ehrliche Fall: Die Datenmenge wächst über Monate, der RAM bleibt, und irgendwann passt das aktive Arbeits-Set schlicht nicht mehr in den Buffer Pool — die PLE sinkt schleichend über Wochen. Bevor du aber RAM bestellst, die Gegenprobe: Liest der Server zu viel, weil Scans ganze Tabellen durch den Cache pumpen, die nur wenige Zeilen liefern? Ein einziger nächtlicher Report mit fehlendem Index kann einen 100-GB-Cache leerfegen wie ein Schneepflug. Die Top-Queries nach total_logical_reads (Stufe zwei der Diagnose-Pyramide) zeigen die Kandidaten; passende Indizes sind dann billiger und nachhaltiger als jede Speichererweiterung. Erst wenn die Lese-Monster gezähmt sind und die PLE trotzdem leidet, ist Hardware das richtige Gespräch.
4. Übergroße Memory Grants — die Schätzung prasst, alle warten
Die zweite Front: RESOURCE_SEMAPHORE-Wartezeiten ohne auffällige PLE. Hier ist nicht der Buffer Pool das Opfer, sondern der Workspace-Topf — meist, weil einzelne Abfragen aufgrund schiefer Schätzungen absurd große Grants anfordern und den Topf leerräumen. Die Wurzeln sind alte Bekannte: veraltete Statistiken, Parameter Sniffing, fehlende Indizes — die ganze Familie aus dem Statistiken-Beitrag. Moderne SQL-Versionen entschärfen das Problem zusätzlich per Memory Grant Feedback (der Server lernt aus zu großen Grants und korrigiert nach unten); auf älteren Ständen bleibt die klassische Therapie: Schätzungen reparieren, notorische Einzelfälle per Query-Hint deckeln. Und nicht vergessen: Zu kleine Grants erzeugen das Spiegelproblem — Spills nach tempdb, nachzulesen im tempdb-Beitrag.
5. Ein Clerk bläht sich auf — der Ad-hoc-Plan-Cache als Mitesser
Wenn die Clerk-Abfrage einen unerwarteten Großverbraucher zeigt, ist es in der Praxis meist der Plan-Cache — vollgestopft mit zehntausenden Einmal-Plänen, weil die Anwendung jede Abfrage als neuen Ad-hoc-String schickt statt parametrisiert. Jeder dieser Pläne wird genau einmal benutzt und verstopft trotzdem den Speicher. Die wirksamste Sofortmaßnahme ist die Serveroption optimize for ad hoc workloads: Erst beim zweiten Auftauchen einer Abfrage wird der vollständige Plan gecacht, Einmal-Gäste hinterlassen nur einen winzigen Stub. Die strukturelle Lösung — Parametrisierung in der Anwendung — gehört auf den Tisch des Herstellers, mit der Clerk-Auswertung als Beweismittel. Seltener, aber prüfenswert: In-Memory OLTP (MEMORYCLERK_XTP) und CLR mit eigenem Appetit.
Was du auf keinen Fall tun solltest
|
Die drei häufigsten Anfängerfehler 1. RAM bestellen, bevor die Druckrichtung bestimmt ist. Gegen Ballooning, schiefe Grants oder einen Ad-hoc-aufgeblähten Plan-Cache hilft neuer Speicher exakt gar nicht — er wird einfach mitgefressen. |
|---|
Nachsorge: Damit es nicht wiederkommt
Drei strukturelle Maßnahmen nach dem akuten Fall: Erstens, die Memory-Kennzahlen in die Baseline aufnehmen — PLE pro NUMA-Knoten, Memory Grants Pending und das Total/Target-Verhältnis regelmäßig wegschreiben. Schleichender Druck ist im Trend Wochen vorher sichtbar, im Momentwert nie. Zweitens, die Spielregeln mit dem Virtualisierungs-Team schriftlich machen: Speicher-Reservierung für Produktions-SQL-VMs, und Änderungen am VM-Speicher gehen vorher an den DBA — die Lead-Szene dieses Beitrags ist zu 100 Prozent vermeidbare Kommunikation. Drittens, die Schätzungs-Hygiene pflegen: aktuelle Statistiken und passende Indizes halten die Grants ehrlich und den Cache schlank — Speicher-Gesundheit ist zur Hälfte Wartungs-Gesundheit. Den Rahmen dafür liefert der Health-Check-Beitrag der GRÜN-Stufe.
Vertiefung in der Buchreihe
Speicherarchitektur, Grants und Cache-Interna füllen in der Buchreihe „SQL Server in der Praxis“ eigene Kapitel — alle Bände in der Übersicht auf der SQL-Pillar-Seite unter /sql-server/.
|
Empfohlener Band Band 1: Performance & Troubleshooting — das Heimatbuch dieses Beitrags: Buffer-Pool-Mechanik und PLE-Interpretation, Memory Clerks im Detail, Grant-Berechnung und Memory Grant Feedback, NUMA-Besonderheiten und die Konfigurationsregeln für physische wie virtualisierte Umgebungen. |
|---|
Verwandte Notfälle im Notarztwagen
Speicherdruck strahlt in alle Richtungen aus — diese Beiträge aus dem Cluster lohnen den Blick:
Drei Wege, wenn der Speicher jetzt gerade drückt
Festpreis-Analyse
Die PLE sägt, keiner weiß seit wann, und die Frage „brauchen wir mehr RAM oder ist etwas kaputt?“ steht unbeantwortet im Raum? Dafür gibt es die Festpreis-Analyse: Ich bestimme die Druckrichtung, werte PLE-Verlauf, Clerks und Grant-Verhalten aus, prüfe Max Server Memory, Lock Pages und die VM-Konfiguration und liefere einen Bericht mit priorisierten Maßnahmen — inklusive einer belastbaren Antwort auf die RAM-Frage, bevor Geld ausgegeben wird. Zum vereinbarten Festpreis, ohne Stundenzähler.
Akut-Hotline
Wenn gerade jetzt Abfragen in der Grant-Warteschlange hängen und das Tagesgeschäft steht: anrufen. Halte einen Zugang mit VIEW-SERVER-STATE-Rechten bereit und die Antwort auf zwei Fragen — ist die Maschine virtualisiert, und was hat sich zuletzt geändert (am Gast oder am Host)? Mit den drei Skripten aus diesem Beitrag ist die Druckrichtung meist in der ersten halben Stunde bestimmt.
Selbst-Diagnose-Kit
Du willst dem Speicher selbst auf den Grund gehen? Das Diagnose-Kit bündelt die Skripte aus diesem Beitrag — Kennzahlen-Übersicht, Clerk-Analyse, Grant-Liste, Max-Server-Memory-Rechner — als ausführbares Paket mit Auswertungshilfe und einer Checkliste für virtualisierte Umgebungen.
|
Ein letzter Tipp Stell deinem Virtualisierungs-Team heute eine einzige Frage: „Hat die SQL-VM eine feste Speicher-Reservierung — ja oder nein?“ Wenn die Antwort zögerlich ausfällt, hast du gerade die wahrscheinlichste Ursache deines nächsten Performance-Rätsels gefunden, bevor es auftritt. Die Lead-Szene dieses Beitrags passiert da draußen jede Woche — fast immer, weil zwei Teams dieselbe Maschine verwalten und nicht miteinander reden. |
|---|
Häufige Fragen (FAQ)
Diese Sektion ist auf häufige Suchanfragen abgestimmt. Sie hilft dir, schnell den Punkt zu finden, der dir gerade akut weiterhilft — und sie ist gleichzeitig die Eingabe für das FAQPage-Schema auf der Seite.
Was ist Memory Pressure im SQL Server?
Memory Pressure heißt: Der verfügbare Speicher reicht für die Arbeitslast nicht mehr aus, und der Server beginnt, Wertvolles zu verdrängen — gecachte Datenseiten fliegen aus dem Buffer Pool, Pläne aus dem Plan-Cache, Abfragen warten auf ihre Speicherzuteilungen. Wichtig ist die Unterscheidung der Richtung: Bei externem Druck nimmt das Betriebssystem oder der VM-Host dem SQL Server Speicher weg; bei internem Druck reicht das eigene Budget (Max Server Memory) für die Workload nicht — Grants und Caches verdrängen sich gegenseitig. Beide Richtungen fühlen sich gleich an, brauchen aber völlig verschiedene Therapien.
Was sagt mir Page Life Expectancy?
Page Life Expectancy (PLE) ist die erwartete Verweildauer einer Datenseite im Buffer Pool, in Sekunden. Hohe Werte heißen: Der Cache ist groß genug, Gelesenes bleibt liegen. Stürzt die PLE regelmäßig ab, werden Seiten verdrängt, kaum dass sie geladen sind — der Server liest dieselben Daten immer wieder von der Platte. Vergiss dabei den alten 300-Sekunden-Grenzwert: Er stammt aus Zeiten von 4-GB-Servern. Aussagekräftig ist die PLE nur relativ zur eigenen Baseline — und auf NUMA-Maschinen pro NUMA-Knoten, denn ein einzelner leidender Knoten verschwindet im Gesamtdurchschnitt.
Wie setze ich Max Server Memory richtig?
Max Server Memory begrenzt im Wesentlichen den Speicher der SQL-Engine — und der Standardwert (praktisch unbegrenzt) ist eine Falle, weil der SQL Server dann dem Betriebssystem die Luft abdrückt. Bewährte Faustregel: Vom physischen RAM zunächst rund 4 GB plus etwa 1 GB je 8 GB RAM für das OS reservieren, den Rest dem SQL Server geben — und nach unten korrigieren, wenn auf der Maschine noch mehr läuft (SSIS, SSRS, Agent-Jobs mit Appetit, eine zweite Instanz). Danach beobachten: Meldet das OS verfügbaren Speicher unter ein paar hundert MB, war das Budget zu großzügig. Das Setz-Kommando steht im Beitrag.
Was bedeutet der Wartetyp RESOURCE_SEMAPHORE?
RESOURCE_SEMAPHORE heißt: Eine Abfrage wartet darauf, ihren Arbeitsspeicher für Sortierungen und Hash-Operationen zugeteilt zu bekommen — den Memory Grant. Der Workspace-Speicher ist ein begrenzter Topf, und wenn gleichzeitig zu viele oder zu große Grants angefordert werden, bildet sich eine Warteschlange: Abfragen stehen, bevor sie überhaupt die erste Zeile gelesen haben. Dominiert dieser Wartetyp, lohnt der Blick auf sys.dm_exec_query_memory_grants — dort siehst du, wer wartet und wer gerade unverschämt viel reserviert hat. Häufigste Wurzel: schiefe Kardinalitätsschätzungen, die absurde Grant-Größen anfordern.
Was sind Memory Grants?
Ein Memory Grant ist die Speicherreservierung, die eine Abfrage vor dem Start für ihre Sortierungen und Hash-Joins bekommt. Die Größe wird aus der geschätzten Zeilenzahl berechnet — und genau da liegt das Drama: Schätzt der Optimizer zu hoch, reserviert die Abfrage ein Vielfaches dessen, was sie braucht, und blockiert den Topf für alle anderen (RESOURCE_SEMAPHORE-Wartezeiten). Schätzt er zu niedrig, ist der Grant zu klein und die Operation spillt nach tempdb. Beides hat dieselbe Wurzel: schlechte Schätzungen durch veraltete Statistiken oder Parameter Sniffing — und beides steht in diesem Beitrag bzw. seinen Nachbarn.
Wie analysiere ich sys.dm_os_memory_clerks?
Die DMV zeigt, welcher interne Verbraucher (Clerk) wie viel Speicher hält — sortiere nach pages_kb absteigend und schau auf die Top Ten. Normalbild: MEMORYCLERK_SQLBUFFERPOOL dominiert mit großem Abstand, danach der Plan-Cache (CACHESTORE_SQLCP, CACHESTORE_OBJCP) in moderater Größe. Verdächtig wird es, wenn andere Clerks in die Größenordnung des Buffer Pools wachsen: ein aufgeblähter Ad-hoc-Plan-Cache, MEMORYCLERK_XTP bei In-Memory OLTP oder Lock-Speicher bei Sperren-Orgien. Das fertige Skript samt Interpretationshilfe findest du im Diagnose-Abschnitt dieses Beitrags.
Wann brauche ich Lock Pages in Memory?
Lock Pages in Memory ist ein Windows-Recht für das SQL-Dienstkonto, das verhindert, dass das Betriebssystem den Buffer Pool in die Auslagerungsdatei wegpagt. Sinnvoll ist es auf dedizierten SQL-Maschinen und besonders in virtualisierten Umgebungen, wo Host-Ballooning den Gast unter Druck setzen kann — dort verhindert es den schlimmsten Fall, dass der komplette Cache auf der Platte landet. Zwei Bedingungen: Max Server Memory muss sauber gesetzt sein (sonst würgst du das OS, das sich nicht mehr wehren kann), und nach der Rechtevergabe ist ein Dienst-Neustart nötig. Auf Maschinen mit vielen Mitbewohnern: erst Hausaufgaben, dann Lock Pages.
