APCu Shared Memory für PHP konfigurieren: FPM, Apache mod_php und CLI im Vergleich
Viele Webanwendungen greifen bei Caching-Bedarf reflexartig zu Redis oder Memcached. Doch für Daten, die auf demselben Server verarbeitet werden — wie Anwendungs-Konfigurationen, Übersetzungs-Tabellen, Routing-Maps oder berechnete Metadaten — erzeugt ein externer Cache unnötigen Overhead: TCP-Handshakes, Serialisierung und Kontextwechsel kosten wertvolle Millisekunden.
Genau hier ist APCu (APC User Cache) unschlagbar: Daten liegen als native C-Strukturen im Shared Memory des Betriebssystems. Ein apcu_fetch() ist kein Netzwerkanruf, sondern ein direkter RAM-Zugriff in Nanosekunden.
1. Abgrenzung: OPcache vs. APCu
Häufig herrscht Verwirrung über die Rollenverteilung:
- OPcache: Cacht ausschließlich vorkompilierten PHP-Bytecode (
.php-Dateien). Er verhindert, dass PHP Skripte bei jedem Aufruf erneut parsen und kompilieren muss. Details dazu findest du in unserem Leitfaden zu PHP-Performance und OPcache. - APCu: Cacht reine Anwendungsdaten (Strings, Arrays, Objekte). Wenn du eine SQL-Abfrage, eine Konfigurationsdatei oder ein berechnetes Token zwischenspeichern willst, ist APCu das Werkzeug der Wahl.
Beide Module arbeiten Hand in Hand und ergänzen sich perfekt.
2. Shared-Memory-Architektur in den verschiedenen PHP-Betriebsarten
Der wichtigste Grund für Missverständnisse bei APCu ist die Annahme, der Cache sei „einfach global da“. In Wahrheit hängt die Lebensdauer und Reichweite des Shared-Memory-Segments strikt davon ab, wie PHP ausgeführt wird:
A. PHP-FPM (Der Standard)
- Architektur: Der
php-fpm-Master-Prozess allokiert beim Booten das SHM-Segment viammap()oder POSIX Shared Memory. Alle Kind-Worker (pm.max_children) greifen auf dieselben Speicheradressen zu. - Reload: Ein Graceful Reload (
systemctl reload php8.3-fpm) startet die Worker neu und leert das APCu-Segment geordnet. - Pool-Grenzen: Jeder FPM-Pool (z. B.
www.confvs.api.conf), der unter einem eigenen Master oder separaten Rechten läuft, kann ein getrenntes Segment besitzen.
B. Apache mit mod_php (Prefork MPM)
- Architektur: Hier ist der Apache-Parent-Prozess der Master. Beim Server-Start reserviert Apache das Shared-Memory-Segment für alle geforkten
httpd-Prozesse. - Reload: Ein
systemctl reload apache2(Graceful Restart) behält das APCu-Segment oft bei, während ein harter Neustart (restart) das Segment komplett neu initialisiert. - Hinweis: Da
mod_phpan das Prefork-MPM gebunden ist (kein Threading), ist der RAM-Verbrauch je Prozess deutlich höher als bei FPM.
C. Moderne Application Server: FrankenPHP, RoadRunner & Swoole
In modernen Setups bleibt die PHP-Laufzeitumgebung als persistenter Daemon dauerhaft im Speicher:
- FrankenPHP / RoadRunner / Workerman: Die Worker-Prozesse beenden sich nach einem Request nicht, sondern bleiben am Leben.
- Die Besonderheit: In solchen Long-Running-Prozessen können einfache PHP-Variablen im Skript-Scope bereits requests-übergreifend gecacht werden!
- Rolle von APCu: Laufen mehrere Worker-Prozesse (Multi-Worker), teilen diese normalen PHP-Speicher nicht. Hier dient APCu weiterhin als Brücke, um Daten zwischen den Workern synchron zu halten, ohne auf externe Dienste wie Redis zurückzugreifen.
D. PHP-CLI (Kommandozeile & Cronjobs)
- Isolierter Speicher: Ein Aufruf auf der Konsole (
php script.phpoder Cronjob) startet einen eigenen, isolierten PHP-Prozess. - Warum
apc.enable_clistandardmäßig aus ist: Wenn ein CLI-Skript endet, stirbt sein Prozess und das Shared-Memory-Segment wird sofort vom Betriebssystem freigegeben. Ein Cache, der beim Skript-Ende erlischt, bringt selten Vorteile. - Wann
apc.enable_cli = 1sinnvoll ist:
- Unit- & Integrationstests: Tests (PHPUnit), die auf APCu-Funktionalität aufbauen.
- Dauerläufer-CLI-Dienste: Eigene Konsolen-Worker oder Message-Consumer, die über Stunden laufen und In-Memory-Lookups brauchen.
- ⚠ Wichtig: Ein Konsolenskript kann niemals auf den APCu-Speicher von PHP-FPM oder Apache zugreifen! Ein Vorwärmen des Web-Caches per CLI-Cronjob ist technisch unmöglich.
3. Installation von APCu
Unter Debian und Ubuntu ist die Installation unkompliziert:
sudo apt update
sudo apt install php8.3-apcu
Prüfe anschließend, ob das Modul in der jeweiligen SAPI aktiv ist:
# Für CLI:
php -m | grep apcu
# Für FPM (z.B. über phpinfo() im Web oder FPM-Test):
php-fpm8.3 -m | grep apcu
4. Die perfekten php.ini-Einstellungen für APCu
Die Standard-Konfiguration der Distributionen reicht für produktive Umgebungen selten aus: Der Standard-Speicher (apc.shm_size = 32M) läuft bei größeren Projekten schnell voll, was zu ständigen Cache-Invalidierungen und Speicher-Fragmentierung führt.
Erstelle oder bearbeite die Konfigurationsdatei /etc/php/8.3/mods-available/apcu.ini:
; APCu aktivieren
extension=apcu.so
apc.enabled=1
; Größe des Shared-Memory-Segments (Standard 32M ist fast immer zu klein!)
; Für produktive Systeme meist 128M oder 256M empfohlen:
apc.shm_size=128M
; Lebensdauer von Einträgen (in Sekunden)
; 0 = Einträge verbleiben bis zum manuellen Löschen oder Server-Neustart
apc.ttl=7200
; Garbage Collection TTL: Wie lange verwaiste Einträge im Speicher verbleiben dürfen
apc.gc_ttl=3600
; Slam Defense: Verhindert "Cache Stampedes" bei stark frequentierten Keys
; Wenn 100 Worker gleichzeitig feststellen, dass ein Key fehlt, berechnet nur einer den Wert neu.
apc.slam_defense=1
; CLI-Zugriff: Im Standard 0. Nur aktivieren (1), wenn du Tests oder Long-Running CLI-Worker nutzt.
apc.enable_cli=0
; Serializer-Backend
apc.serializer=php
Nach Änderungen muss der Webdienst neu geladen werden:
# Bei PHP-FPM:
sudo systemctl reload php8.3-fpm
# Bei Apache mod_php:
sudo systemctl reload apache2
5. Saubere Nutzung im PHP-Code: Das Atomic Cache Pattern
Ein häufiges Anti-Pattern ist das getrennte Prüfen und Schreiben (fetch → if false → store):
// ❌ SCHLECHT: Race Condition bei hohem Traffic (Cache Stampede)
$val = apcu_fetch('my_heavy_key');
if ($val === false) {
$val = calculateExpensiveData(); // 50 Worker tun das gleichzeitig!
apcu_store('my_heavy_key', $val, 3600);
}
Nutze stattdessen das atomare apcu_entry(). Diese Funktion garantiert, dass die Berechnungs-Callback-Funktion genau einmal ausgeführt wird, selbst wenn hunderte Requests gleichzeitig eintreffen:
// RICHTIG: Atomares Laden und Befüllen
$data = apcu_entry('my_heavy_key', function($key) {
// Wird garantiert nur von einem Prozess ausgeführt
return calculateExpensiveData();
}, 3600);
6. Diagnose & Monitoring: Ist der Cache gesund?
APCu bringt mächtige Diagnose-Funktionen mit. In PHP kannst du den Status direkt auslesen:
$sma = apcu_sma_info();
$memUsed = $sma['seg_size'] - $sma['avail_mem'];
$memTotal = $sma['seg_size'];
$percent = round(($memUsed / $memTotal) * 100, 1);
$info = apcu_cache_info(true);
$hits = $info['num_hits'];
$misses = $info['num_misses'];
$hitRate = ($hits + $misses) > 0 ? round(($hits / ($hits + $misses)) * 100, 2) : 100;
echo "APCu Speicherbelegung: {$percent}% ({$memUsed} / {$memTotal} Bytes)\n";
echo "Hit-Rate: {$hitRate}%\n";
Typische Alarmsignale:
- Speicherbelegung nahe 100%:
apc.shm_sizeerhöhen! Wenn APCu vollläuft, beginnt das Segment zu fragmentieren. - Viele Expulsions: Wenn APCu Speicher freiräumen muss, bevor die reguläre
ttlabgelaufen ist, ist der Speicher zu knapp bemessen. - Hohe Miss-Rate: Prüfe, ob Schlüssel mit zufälligen IDs generiert werden, die nie wiederverwendet werden.
7. Wann APCu die falsche Wahl ist
So schnell APCu ist – es gibt Szenarien, in denen du auf Redis oder Datenbank-Caching setzen musst:
- Multi-Server-Umgebungen (Cluster): Wenn deine Anwendung hinter einem Load Balancer auf mehreren Instanzen läuft, hat jeder Server sein eigenes APCu-Segment. Daten sind nicht knotenübergreifend synchron.
- Permanente Persistenz: APCu ist flüchtig. Bei Server-Neustarts oder FPM-Neustarts sind die Daten weg.
- Sehr große Datenmengen: APCu sollte für Hot-Data (Metadaten, Menübäume, Configs) reserviert sein, nicht für Gigabytes an Rohdaten.
Für performancekritische Single-Server-Instanzen ist APCu jedoch der eleganteste, ressourcenschonendste Cache, den das PHP-Ökosystem bietet.
Häufige Fragen (FAQ)
Was ist der Unterschied zwischen APC und APCu?
APC war das historische Gesamtpaket für PHP 5 (Opcode-Cache + User-Cache). Mit PHP 5.5 wurde der Opcode-Teil als offizieller OPcache in den PHP-Kern integriert. Der User-Cache wurde als eigenständige Erweiterung ausgekoppelt und heißt seither APCu.
Warum sieht mein Cronjob oder CLI-Skript die Daten aus dem Webserver nicht?
Weil PHP-CLI und PHP-FPM völlig getrennte Prozessräume sind. Das Shared-Memory-Segment wird pro Master-Prozess verwaltet. Ein CLI-Befehl allokiert bei gesetztem apc.enable_cli=1 ein eigenes temporäres Segment, das beim Skriptende wieder zerstört wird.
Wie leere ich den APCu-Cache im laufenden Betrieb?
Entweder über einen Reload des Dienstes (systemctl reload php8.3-fpm bzw. systemctl reload apache2) oder programmatisch innerhalb eines Web-Requests über den Aufruf von apcu_clear_cache().
Weiterführende Themen & Praxis-Know-how
Eine solide Cache-Architektur entfaltet ihre volle Kraft nur im Zusammenspiel mit sauberem Code und einer abgestimmten Datenbank. Weiterführende Praxis-Anleitungen:
- PHP schneller konfigurieren: OPcache, Preloading, FPM
- PHP-Performance: die acht teuersten Fehler im Code
- PostgreSQL Performance-Settings optimieren
Im Seminar PHP OOP und fortgeschrittene Technologien vertiefen wir hochperformante Anwendungsarchitekturen und Thread-sichere In-Memory-Konzepte in der Praxis.