Ich erkläre, wie du vm.max_map_count auf Linux-Datenbankservern verstehst, misst und ohne Risiko einstellst. Der Beitrag zeigt konkrete Schritte, typische Werte und praxiserprobte Kontrollen, damit PostgreSQL, MySQL/MariaDB, Elasticsearch oder OpenSearch unter Last sauber laufen.
Zentrale Punkte
- Funktion: Obergrenze für Virtual Memory Areas (VMAs) pro Prozess
- Relevanz: Datenbanken, Suchsysteme, Java-Stacks mit vielen Mappings
- Symptome: „Cannot allocate memory“, Startfehler, Abbrüche
- Praxiswerte: 262.144 bis 1.048.576 für große Workloads
- Vorgehen: Bedarf messen, mit Reserve erhöhen, Monitoring einbinden
Was bedeutet vm.max_map_count?
Der Kernel-Parameter legt fest, wie viele Speicherbereiche (VMAs) ein einzelner Prozess maximal anlegen darf. Jede mmap-Operation, jedes geladene Shared-Object, viele Allokationen und Shared-Memory-Blöcke erhöhen diese Zahl. Ich begrenze damit nicht die Menge an RAM, sondern die Anzahl der getrennten Bereiche im virtuellen Adressraum. Große Prozesse können mit wenigen großen Mappings viel Speicher nutzen, während fragmentierte Workloads durch viele kleine Mappings schnell ans Limit kommen. Wer speicherintensive Software betreibt, muss diese Obergrenze kennen, sonst trifft ihn der Fehler erst unter Last.
Warum Datenbankserver davon betroffen sind
Datenbanken und Suchdienste arbeiten stark mit mmap, Shared Memory, Caches und zahlreichen Bibliotheken. PostgreSQL-Instanzen mit vielen Erweiterungen und Verbindungen, MySQL/MariaDB mit Plugins oder Elasticsearch/OpenSearch mit vielen Index-Segmenten erzeugen viele VMAs. Steigt die Zahl nahe an den Grenzwert, scheitern weitere Mappings und der Prozess meldet Speicherfehler. Genau dann starten Dienste nicht, brechen unter Last ab oder verlieren Knoten in Clustern. Ich verhindere solches Verhalten, indem ich die notwendige Obergrenze vorab ermittle und sauber setze.
Symptome und Risiken einer falschen Einstellung
Das häufigste Zeichen für eine zu niedrige Grenze sind Startfehler trotz freien Arbeitsspeichers. Dienste wie Elasticsearch melden „Cannot allocate memory“, obwohl die Maschine noch Ressourcen frei hat. Auch sporadische Prozessabbrüche tauchen auf, sobald intern mehr VMAs gebraucht werden als erlaubt. Ein zu hoher Wert schadet im Regelfall nicht, weil der Kernel nur etwas mehr Verwaltung für vm_area_structs benötigt. Relevant wird das erst, wenn Prozesse wirklich Millionen Mappings anlegen, was typische Datenbank-Workloads meist nicht erreichen.
Werte aus der Praxis und Einordnung
Viele Distributionen setzen konservative Defaults um 65.536, was für einfache Dienste genügt, aber für Such- und Analyse-Lasten eng wird. In typischen Hosting-Setups nutze ich 262.144 als soliden Startwert für größere Stacks. Für sehr große Elasticsearch-/OpenSearch-Instanzen plane ich 1.048.576 ein, sofern Messwerte in diese Richtung zeigen. Ein höherer Wert bringt keinen direkten Leistungsgewinn, er verhindert Fehler, wenn viele Mappings erforderlich sind. Die Linux Kernel Dokumentation und gängige Praxisberichte bestätigen diese Einordnung.
| App-Typ | VMA-Profil (typisch) | Startwert vm.max_map_count | Oberes Limit (bei Bedarf) |
|---|---|---|---|
| Kleine DB / Tools | niedrig-mittel | 65.536 | 262.144 |
| PostgreSQL/ MySQL | mittel-hoch | 262.144 | 524.288 |
| Elasticsearch/ OpenSearch | hoch-sehr hoch | 262.144 | 1.048.576 |
| Große Java-Stacks | mittel-hoch | 262.144 | 524.288 |
Aktuellen Bedarf messen
Vor jeder Änderung prüfe ich die aktuelle Einstellung mit sysctl vm.max_map_count oder per cat /proc/sys/vm/max_map_count. Anschließend ermittle ich den realen Bedarf eines Prozesses mit wc -l /proc/<PID>/maps, idealerweise unter Last. Dieser Wert schwankt je nach Modulen, Caches und Workload, daher beobachte ich über mehrere Lastfenster. Sobald die Spitze 50–70 % der Grenze erreicht, setze ich eine geeignete Reserve. So treffe ich eine fundierte Entscheidung statt zu raten.
So passe ich vm.max_map_count sicher an
Für Tests setze ich den Wert temporär mit sysctl -w vm.max_map_count=262144, was sofort wirkt und beim Neustart verschwindet. Für den Dauerbetrieb trage ich den Wert in /etc/sysctl.conf ein und lade ihn mit sysctl --system neu, damit die Konfiguration bleibt. Große Such-Cluster oder sehr modulare DB-Stacks profitieren je nach Messung von 524.288 bis 1.048.576. Ich erhöhe schrittweise, kontrolliere Logs und beobachte Metriken zur Speichernutzung. So halte ich das Risiko im Betrieb gering und baue planbar Puffer auf.
Best Practices für produktive Umgebungen
Ich messe wiederholt unter typischer und Spitzenlast, statt mich auf Einmalwerte zu verlassen. Die Obergrenze setze ich nicht auf Kante, sondern mit Faktor zwei bis vier über dem beobachteten Peak. In Clustern wähle ich konsistente Werte, damit alle Knoten gleich reagieren und keine Ausreißer erzeugen. Monitoring prüft Fehler zu mmap/malloc sowie den Verlauf der VMA-Anzahl pro Prozess. Vor Live-Schaltungen teste ich neue Werte in Staging mit vergleichbarer Last.
Zusammenspiel mit anderen Kernel-Parametern: Swappiness, Dirty Ratios, File-Limits
vm.max_map_count steht nie isoliert, denn andere Stellschrauben prägen das Verhalten ebenfalls. Die Swappiness entscheidet, wie aggressiv das System Seiten in den Swap legt, was Latenzen erhöhen kann. Dirty Ratios steuern, wann veränderte Seiten zurück auf die Platte wandern und damit IO-Spitzen glätten oder verschärfen. Limits für offene Dateien bestimmen, wie viele Files und Sockets Datenbanken parallel halten dürfen. Ich prüfe diese Parameter gemeinsam, damit keine neue Engstelle entsteht.
Transparent Huge Pages gezielt prüfen
THP beeinflusst das Speichermanagement, indem es große Seiten bündelt und damit Zugriffsmuster verändert. Datenbanken reagieren je nach Workload sensibel auf THP, weshalb ich Status und Modus überprüfe und auf „madvise“ oder „never“ stelle, wenn Latenzen steigen. Details zu Wirkungen und Tuning habe ich in meinem Hinweis zu Transparent Huge Pages zusammengefasst. Wichtig bleibt, die Änderung mit Metriken zu belegen und nicht blind umzustellen. So bleibt das Speicherverhalten nachvollziehbar und reproduzierbar.
VFS-Cache-Druck verstehen
Der VFS-Cache speichert Metadaten und Dateiinhalte im Arbeitsspeicher und konkurriert damit mit Datenbankseiten. Mit dem Parameter für den VFS-Cache-Druck beeinflusse ich, wie schnell das System diesen Cache freigibt. Zu hoher Druck kann IO-Last erhöhen, zu geringer Druck verdrängt DB-Caches und schadet Latenzen. Ich justiere in kleinen Schritten und messe Auswirkungen auf Page-Cache-Hitrate, IO-Wartezeit und Durchsatz. Dieses Feintuning wirkt oft stärker als erwartet, wenn Datenbanken und Filesysteme eng verzahnt arbeiten.
NUMA-Richtlinien und Datenbanken
NUMA-Architekturen verteilen Speicher über Knoten, was Zugriffszeiten beeinflusst. Ohne passende Richtlinien landen Seiten auf „falschen“ Nodes, was Latenzen und Cache-Misses erhöht. Hinweise zu Modi und Policies gebe ich unter NUMA-Richtlinien, inklusive praxisnaher Startparameter. Für große DB-Prozesse setze ich bevorzugte Nodes und überprüfe Interleaving, damit Speicherzugriffe lokal bleiben. Das Zusammenspiel mit vm.max_map_count wirkt positiv, wenn Prozesse viele Mappings über konsistente NUMA-Strategien erhalten.
Wie VMAs entstehen – und warum sie explodieren können
Ich unterscheide drei Hauptquellen für VMAs: (1) dateigebundene Mappings (z. B. Daten- und Indexsegmente von Elasticsearch/OpenSearch), (2) anonyme Mappings über Allocatoren (glibc, jemalloc, tcmalloc) und (3) Stacks für Threads. Viele kleine Shared-Objects, JIT-Code (z. B. in JVMs) und fragmentierte Allokationsmuster sorgen für zusätzliche Bereiche. Jeder Thread bringt mindestens einen Stack-VMA mit; steigt die Zahl der Worker-Threads, wächst auch die VMA-Zahl. Das erklärt, warum Systeme bei gleicher Datenmenge, aber mehr Threads/Plugins früher an Grenzen stoßen.
Wichtig: Ich unterscheide zwischen „viel Speicher“ und „viele Mappings“. Große, zusammenhängende Bereiche sind selten ein Problem. Kritisch wird es, wenn Software für viele kleine Objekte häufig mmap nutzt (Allocator-Strategien), Bibliotheken dynamisch lädt oder sehr viele Dateien parallel mappt.
Der Overhead pro VMA ist moderat (einige hundert Bytes Verwaltungsdaten). Ein höherer Grenzwert vergrößert die theoretisch möglichen Strukturen, ohne den RAM zu belegen, solange Prozesse sie nicht nutzen. Erst wenn wirklich hunderttausende bis Millionen VMAs entstehen, macht sich die Verwaltungsarbeit des Kernels messbar bemerkbar.
Messmethoden vertiefen: Spitzen sicher erfassen
- Ich messe zu mehreren Tageszeiten und unter Peak-Workload (Batch-Läufe, Reindizierung, Wartungsfenster).
- Ich beobachte nicht nur einen Prozess, sondern das gesamte Set kritischer Dienste (DB, Sidecars, Backup-/Monitoring-Agenten).
- Für wiederholbare Ergebnisse trenne ich „kalt“ (leerem Page-Cache) vs. „warm“ (gefülltem Cache) und dokumentiere Unterschiede.
Praktische Helfer, um VMA-Hotspots zu finden:
# Top-10 Prozesse nach VMA-Anzahl
for p in /proc/[0-9]*; do
pid=${p##*/}; test -r "$p/maps" || continue
c=$(wc -l < "$p/maps" 2>/dev/null || echo 0)
cmd=$(tr -d '\0' < "$p/cmdline" | sed 's/ / /g')
printf "%8d %8d %s\n" "$pid" "$c" "${cmd:-[$(cat $p/comm 2>/dev/null)]}"
done | sort -k2,2nr | head -n 10
Bei Clustern werte ich die Ergebnisse über mehrere Knoten aus und suche systematische Ausreißer (z. B. bestimmte Shards, spezielle Erweiterungen oder Versionen). Ich setze Alarme, wenn ein Prozess >70 % der Grenze erreicht oder wenn die Spitzenauslastung trendartig wächst.
Fehlersuche: typische Logmeldungen und Checks
Wenn die Grenze greift, sehe ich häufig „Cannot allocate memory“, „mmap failed“, „failed to map segment from shared object“ oder Startabbrüche ohne klare RAM-Knappheit. Ich prüfe dann:
grep -i mmap /var/log/*und dienstspezifische Logs auf ENOMEM-Hinweise- Aktuelle Zahl der Mappings:
wc -l /proc/<PID>/maps - Ulimit/Nofile-Limits, weil viele Segmentdateien ohne ausreichend offene Files nicht sinnvoll gemappt werden
- Thread-Zahl (
ps -eLo pid,comm,nlwp | sort -k3 -nr | head), da viele Threads die VMA-Zahl erhöhen
Ich korreliere diese Funde mit Lastprofilen (Index-Builds, Vacuum/Analyze, Großimporte). Zeigt die VMA-Zahl saubere Peaks zu definierten Jobs, dimensioniere ich die Reserve passend.
Container, Clouds und Orchestrierung: Besonderheiten
In Containern ist vm.max_map_count in der Praxis meist eine Host-Einstellung. Ich setze den Wert auf dem Node (Bare Metal oder VM) per sysctl und lade ihn dauerhaft über /etc/sysctl.conf oder Dateien in /etc/sysctl.d/. In Docker-Umgebungen kann ich zwar --sysctl angeben, real wirkt vm.max_map_count aber hostweit – ich plane die Änderung also als Node-weite Maßnahme ein. In Orchestratoren (z. B. Kubernetes) setze ich den Wert bevorzugt per Node-Init/Cloud-Init oder Machine-Image, damit Pods ohne Privilegien sauber starten. Wichtig: Ich dokumentiere die gewählte Compliance-Linie (welcher Node-Typ führt welchen Wert), damit Scheduling und Auto-Scaling konsistent bleiben.
Automatisierung und Compliance
Ich halte die Einstellung „als Code“ fest, etwa in Konfigurations-Management. Beispielhaft nutze ich eine sysctl-Drop-In-Datei:
# /etc/sysctl.d/90-db-mappings.conf
vm.max_map_count = 524288
Der Rollout erfolgt kontrolliert (Staging → Canary → Breitenrollout). Ich prüfe unmittelbar nach Deployments per Health-Checks und validiere, dass neue Pods/Services dieselbe Grenze sehen. Für Audits hinterlege ich Messdaten (Peak-VMAs, Reserve-Faktor, Datum der letzten Anpassung) in der Betriebsdokumentation.
Abstimmung mit Overcommit und OOM-Killer
Eine höhere VMA-Grenze senkt nicht die RAM-Belegung, aber sie erlaubt mehr Mappings. In Arbeitsspitzen kann das Zusammenspiel mit Overcommit-Strategien und dem OOM-Killer relevant werden: Erlaube ich mehr Mappings, können Prozesse aggressiver reservieren. Ich halte daher vm.overcommit_memory und vm.overcommit_ratio im Blick und sorge für klare Reserven (Swap/Headroom) oder restriktivere Overcommit-Policies, wenn Workloads zum Überbuchen neigen. Ziel ist ein Vorwarnfenster: statt abruptem OOM erhalte ich frühzeitig steigende Fehlerraten/Latency-Signale im Monitoring, die auf Gegenmaßnahmen hinweisen.
Edge Cases: 32‑bit, viele Threads, Allocator-Wahl
- 32‑bit Prozesse: Der virtuelle Adressraum ist enger, Fragmentierung fällt schneller ins Gewicht. Eine höhere vm.max_map_count heilt keinen Adressraummangel – hier helfen 64‑bit Builds oder Architekturwechsel.
- Thread-reiche Dienste: Jeder Thread bringt mindestens einen eigenen Stack-VMA. Steigt die Worker-Zahl stark, wächst die VMA-Zahl linear mit. Ich stelle sicher, dass Thread-Pools begrenzt und sinnvoll skaliert sind.
- Allocator: Manche Allocatoren nutzen
mmapexzessiv für große oder viele kleine Blöcke. Bei auffälligen VMA-Spitzen teste ich alternative Allocatoren oder deren Tuning-Optionen, um die Anzahl der Mappings zu reduzieren. - Shared Libraries: Viele kleine, dynamisch geladene Module treiben die Zahl der Mappings hoch. Ich überprüfe, ob sich Module konsolidieren oder unnötige Plugins entfernen lassen.
Checkliste vor der Änderung
- Aktuelle Grenze ermitteln und dokumentieren
- Prozess-spezifische VMA-Peaks über mehrere Lastfenster messen
- Reserve ableiten (Faktor 2–4 über Peak) und Staging-Tests planen
- Begleitende Limits (nofile), Thread-Zahlen, THP, Swappiness, Dirty-Ratios prüfen
- Monitoring/Alerts auf VMA-Nähe, mmap-Fehler und OOM-Events aktivieren
- Rollout- und Rollback-Pfad definieren (Canary, Wartungsfenster, sysctl.d-Dateien)
- Cluster-/Node-Consistency sicherstellen und dokumentieren
Planung für Cluster und Wachstum
Ich betrachte nicht nur den Ist-Zustand, sondern auch erwartetes Wachstum der Daten und Indizes. Neue Features, mehr Mandanten oder zusätzliche Erweiterungen erhöhen oft die Zahl der Mappings. Daher rechne ich mit einem Puffer über dem beobachteten Peak und dokumentiere die Entscheidung sauber. In Clustern halte ich die Werte synchron, damit Knoten identisch reagieren und Failover nicht an Limits scheitert. Regelmäßige Überprüfung in Wartungsfenstern sichert die Kontinuität der Einstellungen.
Kurz zusammengefasst: sichere Einstellung für Datenbankserver
Ich prüfe die aktuelle Grenze, messe die VMA-Anzahl unter Last und setze vm.max_map_count mit Reserve. Für viele Datenbank- und Such-Workloads funktionieren 262.144 als Startwert und 1.048.576 als obere Stufe, falls Messwerte und Wachstum das erfordern. Die Änderung bewirkt keine sofortige Mehrleistung, sie verhindert Fehler, sobald sehr viele Mappings gebraucht werden. Stabilität entsteht, wenn ich Logs, Metriken und verwandte Kernel-Parameter gemeinsam betrachte. So bleibt der Datenbankbetrieb belastbar, planbar und bereit für steigende Lasten.


