Der oft übersehene Hebel für schnelle PHP-Requests heißt PHP Realpath Cache: Er speichert aufgelöste Pfade im Arbeitsspeicher und reduziert teure Dateisystem-Abfragen bei include/require. In Projekten mit Symfony, Laravel oder einem großen WordPress-Setup steigere ich mit einer sauberen Realpath-Konfiguration die Performance messbar und halte Systemcalls pro Request deutlich niedriger.
Zentrale Punkte
- Einfacher Hebel: Realpath speichert Pfadauflösungen und spart Dateisystem-Zugriffe.
- Per-Worker: Jeder PHP-FPM-Prozess führt seinen eigenen Realpath Cache.
- Größe zählt: Zu kleiner Cache verursacht Thrashing und bremst Requests.
- TTL steuert Aktualität: Lange TTL für stabile Deployments, kurze bei Symlinks/Secrets.
- Monitoring: realpath_cache_get()/size() zeigen Auslastung und Lücken.
Was der Realpath Cache genau leistet
Bei jedem include, require oder file_get_contents löst PHP relative Pfade zu absoluten Pfaden auf und speichert diese Ergebnisse im Cache. Trifft derselbe Pfad erneut ein, lese ich das Resultat aus dem Speicher und spare mir den teuren Gang ins Dateisystem. Dieser Mechanismus reduziert Systemcalls spürbar, vor allem wenn Composer-Autoloading viele Klassen und Konfigurationsdateien lädt. Wichtig: Der Realpath Cache existiert pro Prozess, daher profitiert jeder PHP-FPM-Worker erst nach einigen Requests, wenn er seinen eigenen Cache aufgebaut hat. So entsteht ein stetiger Beschleunigungseffekt, der sich bei hoher Last besonders auszahlt.
Warum der Cache in großen Frameworks zählt
Große Frameworks und viele Plugins erzeugen pro Request zahllose Dateizugriffe, die ohne Caching jedes Mal Pfade neu auflösen müssten. Wenn der Realpath Cache zu klein ist, verdrängt er ältere Einträge, neue kommen dazu, und ich beobachte pures Thrashing. Das Ergebnis sind wiederholte Stat- und Lookup-Operationen, die Zeit kosten und IO belasten. Praxisnah lässt sich damit die Anzahl der Systemcalls pro Request um etwa 5–15 % senken, was sich bei hoher Request-Rate massiv summiert. Je modularer die Anwendung, desto größer der Hebel über einen sauber dimensionierten Realpath Cache.
So ermittle ich die passende Cache-Größe
Ich zähle zuerst die einzigartigen Pfade eines typischen Requests, schätze die mittlere Pfadlänge und addiere pro Eintrag rund 128 Bytes Overhead. Aus Anzahl, Pfadlänge und Overhead leite ich eine realpath_cache_size ab, die genügend Puffer bietet. Viele größere Projekte landen zwischen 4 und 16 MiB, sehr umfangreiche Monorepos auch darüber. Wichtig ist, dass der Cache nicht am Limit klebt, sonst verliere ich den Nutzen durch ständiges Ausräumen. Ich erhöhe in Schritten, beobachte die Auslastung und reguliere nach.
Empfohlene Einstellungen und Beispielwerte
Standardwerte stammen aus Zeiten kleiner Codebasen und passen oft nicht mehr zu heutigen Setups. Für viele produktive Anwendungen setze ich realpath_cache_size auf 4096K bis 16384K und verlängere realpath_cache_ttl auf 360–600 Sekunden oder mehr. Entscheidend sind Projektgröße, Deploy-Frequenz und Dateisystem-Charakter. Die folgende Tabelle zeigt sinnvolle Anhaltswerte zur Orientierung und hilft beim Start ins Tuning. Danach passe ich die Zahlen über Monitoring und Lasttests an.
| Einstellung | Häufiger Default | Gute Startwerte | Erwartete Wirkung |
|---|---|---|---|
| realpath_cache_size | 4096K (4 MiB) | 4096K–16384K | Reduziert Thrashing bei vielen Dateien |
| realpath_cache_ttl | 120–600 s | 360–900 s | Längere Cache-Halts, weniger erneute Auflösungen |
Beispiele in der php.ini: realpath_cache_size = 4096K und für große Frameworks realpath_cache_size = 16384K. Für die Lebensdauer nutze ich häufig realpath_cache_ttl = 360 oder höher bei seltenen Releases. So bleiben Pfade über viele Requests im Speicher, ohne ständig neu validiert zu werden.
TTL klug wählen – je nach Deployment
Die richtige TTL hängt stark vom Deploy-Prozess und dem Einsatz von Symlinks ab. Wenn ich Releases per Symlink-Rotation umschalte, darf der Cache keine veralteten Pfade liefern, daher setze ich die TTL kurz oder triggere einen FPM-Neustart nach dem Rollout. In Kubernetes-Umgebungen mit Secrets oder ConfigMaps als Volumes reduziere ich die TTL deutlich oder deaktiviere den Realpath Cache zeitweise. Statischere Webhosting-Situationen profitieren dagegen von längeren TTLs, weil sich Pfade selten ändern. So balanciere ich Aktualität und Geschwindigkeit passend zur Umgebung.
Überwachen und verifizieren
Ich prüfe regelmäßig mit realpath_cache_get(), welche Pfade im Cache liegen, und mit realpath_cache_size(), wie viel Speicher davon belegt ist. Wenn die Nutzung nahe der konfigurierten Größe liegt, erhöhe ich die Kapazität schrittweise. Läuft der Cache extrem schnell voll, deute ich das als Signal für mehr Speicher oder für zu kurze TTL. Nach größeren Plugin-Installationen oder Framework-Updates kontrolliere ich erneut. Nur wer die Zahlen kennt, trifft sinnvolle Tuning-Entscheidungen.
Messbare Wirkung: So messe ich Systemcalls und Latenzen
Damit das Tuning belastbar ist, messe ich vor und nach Änderungen. Auf Linux erfasse ich Dateisystem-Calls pro Request mit strace oder perf, wahlweise an einem einzelnen FPM-Worker oder an der CLI.
- Einzelner Request (CLI):
strace -c -o /tmp/strace.txt php public/index.phpliefert eine Übersicht, wie vielestat(),openat()undlstat()anfallen. - FPM-Worker anhängen:
strace -fp <PID> -e trace=file -o /tmp/strace-fpm.logzeigt nur dateibezogene Aufrufe. Vorher mitpsdie PID des Workers ermitteln. - Lasttest: Mit Tools wie
aboderheysimuliere ich Last und vergleiche P95/P99-Latenzen bei unterschiedlichen Cache-Größen.
Parallel dazu lasse ich mir aus PHP die Cache-Belegung anzeigen, zum Beispiel in einem Debug-Endpoint oder per CLI:
<?php
$entries = realpath_cache_get();
$size = realpath_cache_size();
printf("Entries: %d, Used: %d bytes (%.2f MiB)\n", count($entries), $size, $size/1048576);
So erkenne ich, ob die Erhöhung der realpath_cache_size tatsächlich die Misses und Dateisystem-Calls senkt, statt nur RAM zu binden. Idealerweise steigt die Hitrate, während P95-Latenzen merklich fallen.
So wärme ich den Realpath Cache gezielt an
Weil der Cache pro Worker entsteht, lohnt sich ein Warm-up nach dem Deploy oder Neustart. Ziel ist, dass die häufigsten Includes früh im Cache landen, bevor echte Nutzer-Traffic kommt.
- Request-Replays: Ich feuere eine Handvoll typischer URLs (Frontend, Admin, API) nach dem Rollout automatisiert ab.
- CLI-Priming: Ein kurzes Bootstrapping-Script lädt zentrale Pfade (Autoloader, Kernel, Konfiguration, Routen).
<?php
// warmup.php
require __DIR__.'/vendor/autoload.php'; // Composer
require __DIR__.'/config/bootstrap.php'; // Projektabhängig
require __DIR__.'/public/index.php'; // Front-Controller (kann kurzen Lauf triggern)
echo sprintf("Primed %d entries, used %d bytes\n",
count(realpath_cache_get()), realpath_cache_size());
Dieser Warm-up trifft zwar nicht jeden realen Request, legt aber die meistgenutzten Pfade ab und verkürzt die anfängliche Kaltstart-Phase je Worker spürbar.
Zusammenspiel mit OPcache und Filesystem-Cache
OPcache beschleunigt das Ausführen von PHP-Dateien, während Realpath den Weg zur Datei verkürzt, daher kombiniere ich beide Techniken. Für OPcache-Einstellungen nutze ich bewährte Werte und verweise auf fundierte OPcache-Optimierung, damit Bytecode und Pfade ideal zusammenspielen. Zusätzlich profitiert Realpath von einem warmen OS-Cache, der Directory-Lookups und Metadaten schnell liefert. So vermeide ich doppelte Wartezeiten beim Laden und beim Parsen. Wer beide Ebenen koordiniert, holt spürbare Antwortzeitgewinne heraus.
Composer-Autoloading optimal nutzen
Der Composer-Autoloader ist ein Haupttreiber für Pfadauflösungen. Je deterministischer er arbeitet, desto leichter hat es der Realpath Cache.
- Classmap optimieren:
composer dump-autoload -oreduziert Verzeichnis-Scans und wiederholte Lookups. - Autoload strikt halten: Mit
classmap-authoritative(Projekt-Setting) vermeide ich unnötige Fallbacks, die sonst zusätzliche Pfadauflösungen auslösen. - Struktur ordnen: Flache, konsistente Ordnerhierarchien und wenige Sonderfälle (z. B. modul- oder mandantenübergreifende Includes) stabilisieren die Cache-Last.
Das Ergebnis: weniger unterschiedliche Pfade pro Request, mehr Wiederverwendung im Realpath Cache und damit geringere IO-Kosten.
FPM und Memory-Budget: was pro Worker realistisch ist
Weil der Realpath Cache pro Prozess existiert, multipliziert sich die konfigurierte Größe mit der Zahl der FPM-Worker. Ich plane deshalb ein Budget:
- Beispiel: 12 Worker × 8 MiB = 96 MiB Realpath-Kopf; dazu kommen OPcache, PHP-Heap und Extension-Overhead.
- Ausbalancieren: Hat OPcache reichlich Luft, kann Realpath einige MiB mehr bekommen – oder umgekehrt.
- Pool-spezifisch: Unterschiedliche FPM-Pools (Front, Admin, API) dürfen verschiedene Realpath-Größen haben, orientiert am jeweiligen Code-Footprint.
Wächst der Code mit jedem Release, wächst der realpath_cache_size-Bedarf typischerweise mit. Ich prüfe daher regelmäßig die Peak-Belegung unter Last, nicht nur im Leerlauf.
Sonderfälle: Symlinks, Container und NFS
Bei Symlink-Rollouts stelle ich eine kurze TTL ein oder starte FPM nach dem Deploy neu, damit alle Worker frische Pfade laden. In Containern mit veränderlichen Volumes achte ich darauf, dass der Cache nicht veraltete Ziele ausliefert, indem ich die TTL anpasse. Auf NFS empfiehlt sich zusätzlich eine saubere OPcache-Strategie und möglichst wenig Verzeichniswechsel. Wenn Pfade zur Laufzeit wechseln, leere ich im Zweifel gezielt mit clearstatcache(true) auch den Realpath-Teil. Klare Deploy-Regeln verhindern inkonsistente Zustände quer über Worker hinweg.
Typische Stolpersteine und Grenzen
Weil der Cache pro Prozess lebt, muss jeder Worker zunächst einmal Pfade sammeln, bevor der Effekt greift. In Setups mit starken open_basedir-Beschränkungen arbeitet der Realpath Cache eingeschränkt, daher berücksichtige ich diese Grenzen bei der Planung. Zu kleine Caches führen zu Thrashing, zu große Caches verschwenden RAM – ich suche die Spitze der Kurve mit Messungen. Außerdem beachte ich, dass Realpath kein Metadaten- oder Inhalts-Cache ist, sondern ausschließlich Pfadauflösungen speichert. Wer falsche Erwartungen hat, übersieht Ursachen an anderer Stelle.
Betriebssicherheit: typische Fehlerbilder und schnelle Checks
Einige Symptome deuten klar auf Realpath-Probleme hin – und lassen sich schnell verifizieren:
- Sprunghafte Latenzen nach Deploy: Entweder zu lange TTL bei Symlink-Rotation oder fehlender Warm-up. Abhilfe: kurze TTL, FPM-Restart, anschließendes Priming.
- Viele wiederholte
stat()-Aufrufe: Mitstracesichtbar; oft ist die Cache-Größe zu klein oder bestimmte dynamische Pfade verdrängen Hot-Entries. - Hohe Varianz zwischen Workern: Unterschiedliche Caches pro Prozess. Abhilfe: konsistenter Warm-up und gleichmäßige Request-Verteilung.
Ein schneller Health-Check per PHP genügt häufig:
<?php
$entries = realpath_cache_get();
$byDir = 0; $byFile = 0;
foreach ($entries as $e) { $e['is_dir'] ? $byDir++ : $byFile++; }
printf("Dirs: %d, Files: %d, Used: %.2f MiB\n", $byDir, $byFile, realpath_cache_size()/1048576);
So sehe ich, ob vor allem Verzeichnisse (viele Module, Vendor-Strukturen) oder Dateien (zahlreiche Konfigs/Klassen) den Cache dominieren – und passe Struktur oder Größe an.
Stat-Cache vs. Realpath-Cache: bewusst unterscheiden
PHP führt neben dem Realpath Cache auch einen Stat-Cache für Ergebnisse von stat() und verwandten Aufrufen. Beide Caches lassen sich mit clearstatcache() beeinflussen:
clearstatcache()leert den Stat-Cache (optional für eine konkrete Datei).clearstatcache(true)leert zusätzlich den Realpath Cache.
In seltenen Fällen – etwa bei long-running CLI-Workern mit dynamischen Mounts oder bei Hot-Swaps – nutze ich einen gezielten clearstatcache(true)-Hook nach bekannten Änderungen. Ansonsten lasse ich die TTL arbeiten und vermeide unnötige Invalidierungen.
Praxis-Check für Hosting-Umgebungen
Ich ermittle zuerst die Projektgröße, also wie viele Dateien ein typischer Request lädt, und prüfe danach die Auslastung des Caches. Anschließend wähle ich eine realpath_cache_size, die alle häufig genutzten Pfade plus Reserve fasst, und setze eine TTL, die zu Deploy-Rhythmen passt. Danach beobachte ich die Effekte mit Monitoring und Logs und passe Werte behutsam an, statt grob zu erhöhen. Zusätzlich lohnt sich ein Blick auf den OS-Cache, etwa auf die Linux-Einstellung VFS-Cache-Pressure, weil Realpath von schnellen Verzeichnis-Lookups profitiert. So schichte ich Verbesserungen, ohne Nebeneffekte zu übersehen.
Scope und Konfigurationswege: wo ich welchen Wert setze
Je nach Umgebung pflege ich die Parameter an unterschiedlichen Stellen:
- Global:
php.inifür systemweite Defaults. - Pro-Pool: In FPM-Pools per
php_admin_value[realpath_cache_size]undphp_admin_value[realpath_cache_ttl]gezielt für Frontend/API unterschiedlich dimensionieren. - Pro-Verzeichnis: In
.user.ini(falls erlaubt), sinnvoll in Shared-Hosting-Umgebungen.
Wichtig: Änderungen an der php.ini und FPM-Pool-Configs erfordern einen Neustart bzw. Reload, damit Worker mit den neuen Werten starten.
CLI, Queue-Worker und Cronjobs: dieselben Regeln, andere Laufzeiten
CLI-Skripte und Queue-Worker profitieren ebenfalls vom Realpath Cache – allerdings ist die Lebensdauer oft anders:
- Kurzlebige CLI-Jobs: Pro Aufruf entsteht der Cache neu. Hier bringen Warm-up und große TTL wenig, wichtig ist eher eine ausreichende Größe, damit wiederholte Includes im Job selbst cachen.
- Daemons/Worker: Langlaufende Prozesse (Supervisor, Systemd) bauen einen stabilen Cache auf. Nach einem Code-Reload (Deploy) sollte der Prozess neu starten, sonst könnten veraltete Pfade im Cache kleben.
Projektgrößen einschätzen und Speicherwirkung
Eine Anwendung mit 4000 einzigartigen Pfaden und 80 Byte Pfadlänge plus 128 Byte Overhead pro Eintrag benötigt grob 832 KB Speicher im Realpath Cache; mit Reserve plane ich 4 MiB oder mehr. Wächst die Codebasis durch Plugins oder Module deutlich, skaliere ich linear mit und überprüfe erneut die Hits versus Misses. Auf Shared-Hosts beachte ich zusätzlich inode-Grenzen, weil zu viele kleine Dateien das Gesamtsystem belasten; dazu hilft mir dieser Überblick zu Inodes-Limits. Rechne lieber mit Puffer, als permanent am Limit zu laufen. So spare ich Systemcalls ohne unnötigen RAM-Verbrauch.
Kurz und knapp: Mein Tuning-Fahrplan
Ich messe zuerst Dateianzahl pro Request, dann setze ich eine passende Cachegröße und eine zur Deploy-Praxis passende TTL. Anschließend kontrolliere ich mit realpath_cache_get()/size() die Auslastung, justiere in Stufen nach und kombiniere das Ganze mit OPcache- und OS-Cache-Feinschliff. Bei Symlink-Rollouts halte ich die TTL kurz oder starte FPM neu, in statischen Umgebungen nutze ich lange Lebenszeiten. Ziel bleibt eine hohe Cache-Hitrate ohne RAM-Verschwendung. So ziehe ich aus dem Realpath Cache einen unterschätzten Turbo für konsistente PHP-Leistung.


