Symfony Session Handler: Redis vs Memcached vs Datenbank vs Dateien

Oder: Warum “nimm einfach Redis” nicht immer die Antwort ist

“Wir skalieren auf mehrere Server. Sollen wir auf Redis Sessions umsteigen?”

Diese Frage habe ich dutzende Male gehört. Die Antwort ist fast immer “kommt drauf an”—aber nicht auf die frustrierende, unverbindliche Art. Es gibt klare Kriterien, und wenn du die kennst, ist die Wahl einfach.


Die vier Optionen

Permalink to "Die vier Optionen"

Symfony bringt vier Session Handler von Haus aus mit:

Handler Speicher Skalierung Persistenz
NativeFileSessionHandler Lokales Dateisystem Einzelner Server Überlebt Neustarts
PdoSessionHandler Datenbank (MySQL, PostgreSQL) Multi-Server Überlebt Neustarts
MemcachedSessionHandler Memcached Server Multi-Server Verloren bei Neustart
RedisSessionHandler Redis Server Multi-Server Konfigurierbar

Jeder hat seine Vor- und Nachteile.


Datei Sessions: Der zuverlässige Standard

Permalink to "Datei Sessions: Der zuverlässige Standard"
# config/packages/framework.yaml
framework:
  session:
    handler_id: null # Nutzt PHPs Standard-Datei-Handler
    save_path: "%kernel.project_dir%/var/sessions/%kernel.environment%"

Geeignet für:

  • Single-Server-Deployments
  • Entwicklungsumgebungen
  • Anwendungen, bei denen Einfachheit wichtiger ist als Skalierung

Vorteile:

  • Keine Konfiguration nötig
  • Kampferprobte Zuverlässigkeit
  • Eingebautes Session Locking (keine Race Conditions)
  • Überlebt Server-Neustarts

Nachteile:

  • Sessions können nicht über mehrere App-Server geteilt werden
  • Dateisystem-I/O kann zum Flaschenhals werden
  • Session-Dateien sammeln sich an (braucht Garbage Collection)

Produktions-Tipp: Wenn du auf einem einzelnen Server bist und er die Last bewältigt, sind Datei-Sessions völlig in Ordnung. Kein Overkill nötig.


Datenbank Sessions: ACID-Garantien

Permalink to "Datenbank Sessions: ACID-Garantien"
# config/packages/framework.yaml
framework:
  session:
    handler_id: Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler
// config/services.yaml
services:
    Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler:
        arguments:
            - '%env(DATABASE_URL)%'
            - lock_mode: 1  # LOCK_ADVISORY

Geeignet für:

  • Multi-Server-Deployments, wenn du bereits eine Datenbank hast
  • Anwendungen, die transaktionale Konsistenz erfordern
  • Wenn Session-Daten Infrastrukturänderungen überleben müssen

Vorteile:

  • Funktioniert über mehrere App-Server
  • ACID-Garantien (Sessions werden nicht korrupt)
  • Konfigurierbare Locking-Modi
  • Sessions überleben Neustarts und Deployments

Nachteile:

  • Erhöht Datenbank-Last
  • Lock-Konflikte bei vielen parallelen Requests
  • Langsamer als In-Memory-Lösungen

Lock-Modi erklärt

Permalink to "Lock-Modi erklärt"

PdoSessionHandler bietet drei Lock-Modi:

use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler;

// Kein Locking - am schnellsten, aber Race Conditions möglich
PdoSessionHandler::LOCK_NONE

// Advisory Locking - ausgewogener Ansatz
PdoSessionHandler::LOCK_ADVISORY

// Transaktionales Locking - am sichersten, aber am langsamsten
PdoSessionHandler::LOCK_TRANSACTIONAL

Meine Empfehlung: Starte mit LOCK_ADVISORY. Geh nur auf LOCK_NONE runter, wenn du profiliert und bestätigt hast, dass es ein Flaschenhals ist—und du die Race-Condition-Risiken verstehst.


Redis Sessions: Die skalierbare Wahl

Permalink to "Redis Sessions: Die skalierbare Wahl"
# config/packages/framework.yaml
framework:
  session:
    handler_id: Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler
// config/services.yaml
services:
    Redis:
        class: Redis
        calls:
            - connect: ['%env(REDIS_HOST)%', '%env(int:REDIS_PORT)%']

    Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler:
        arguments:
            - '@Redis'

Geeignet für:

  • High-Traffic-Anwendungen
  • Horizontale Skalierung mit mehreren App-Servern
  • Wenn du Sub-Millisekunden-Session-Reads brauchst

Vorteile:

  • Extrem schnell (In-Memory)
  • Skaliert horizontal
  • Kann auf Disk persistieren (RDB/AOF)
  • Unterstützt Clustering

Nachteile:

  • Kein natives Session Locking (das ist ein großes Problem)
  • Zusätzliche Infrastruktur zu warten
  • Speicherkosten bei großen Session-Daten

Das Locking-Problem

Permalink to "Das Locking-Problem"

Das verdient einen eigenen Artikel (kommt als nächstes), aber hier die Zusammenfassung:

Redis hat kein Session Locking. Es kann zu Race Conditions kommen, wenn du auf Sessions zugreifst. Zum Beispiel “Invalid CSRF token”-Fehler. — Symfony Dokumentation

Wenn deine App parallele AJAX-Requests macht, die Session-Daten ändern, können Redis Sessions kaputtgehen. Das Symptom: zufällige Logouts, verlorene Flash-Messages, CSRF-Fehler.

Workarounds:

  • Locking selbst in der App bauen
  • session_write_close() früh aufrufen
  • Einen Redis Session Handler mit Locking nutzen (z.B. snc/redis-bundle)

Memcached Sessions: Einfaches Caching

Permalink to "Memcached Sessions: Einfaches Caching"
# config/packages/framework.yaml
framework:
  session:
    handler_id: Symfony\Component\HttpFoundation\Session\Storage\Handler\MemcachedSessionHandler
// config/services.yaml
services:
    Memcached:
        class: Memcached
        calls:
            - addServer: ['%env(MEMCACHED_HOST)%', '%env(int:MEMCACHED_PORT)%']

    Symfony\Component\HttpFoundation\Session\Storage\Handler\MemcachedSessionHandler:
        arguments:
            - '@Memcached'

Geeignet für:

  • Einfache verteilte Caching-Anforderungen
  • Wenn du bereits Memcached für anderes Caching hast
  • Anwendungen, bei denen Session-Verlust verkraftbar ist

Vorteile:

  • Schnell (In-Memory)
  • Einfaches Protokoll
  • Funktioniert über mehrere Server

Nachteile:

  • Keine Persistenz (Sessions bei Neustart verloren)
  • Kein natives Locking (gleiche Race-Condition-Probleme wie Redis)
  • LRU-Eviction kann Sessions zufällig löschen bei Speicherdruck

Meine Meinung: Wenn du nicht bereits Memcached in deinem Stack hast, bevorzuge Redis. Es macht alles was Memcached macht, plus Persistenz und mehr Datenstrukturen.


Die Entscheidungsmatrix

Permalink to "Die Entscheidungsmatrix"
Anforderung Beste Wahl
Einzelner Server, einfache App Dateien
Multi-Server, Zuverlässigkeit wichtig Datenbank (PDO)
Multi-Server, Geschwindigkeit wichtig Redis (mit Locking-Bewusstsein)
Bereits Memcached im Einsatz Memcached (aber Redis in Betracht ziehen)
ACID-Garantien erforderlich Datenbank (PDO)
Sub-Millisekunden-Reads Redis oder Memcached
Session-Daten müssen Neustarts überleben Dateien, Datenbank oder Redis mit Persistenz

Zwischen Handlern migrieren

Permalink to "Zwischen Handlern migrieren"

Beim Wechsel des Session Handlers werden normalerweise alle ausgeloggt. Symfonys MigratingSessionHandler löst das:

use Symfony\Component\HttpFoundation\Session\Storage\Handler\MigratingSessionHandler;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler;

// Liest vom alten Handler, schreibt in beide
$handler = new MigratingSessionHandler(
    new PdoSessionHandler($pdo),      // Alter Handler (liest zuerst von hier)
    new RedisSessionHandler($redis)   // Neuer Handler (Schreibvorgänge gehen an beide)
);

Lass das für eine Session-Lifetime laufen (z.B. 24 Stunden), dann wechsle zu Redis-only:

$handler = new RedisSessionHandler($redis);

Zero-Downtime-Migration ohne jemanden auszuloggen.


Meine Empfehlungen

Permalink to "Meine Empfehlungen"

Für die meisten Anwendungen

Permalink to "Für die meisten Anwendungen"

Starte mit Datei Sessions. Wenn du nicht mehrere App-Server betreibst, brauchst du keinen verteilten Session-Speicher. Datei Sessions sind zuverlässig, schnell genug und haben ordentliches Locking.

Wenn du skalierst

Permalink to "Wenn du skalierst"

Wechsle zuerst zu Datenbank Sessions. Du hast bereits eine Datenbank. Die zusätzliche Last ist normalerweise vernachlässigbar, und du bekommst ordentliches Locking. Wechsle nur zu Redis, wenn du gemessen und bestätigt hast, dass die Datenbank ein Flaschenhals ist.

Für High-Traffic-Anwendungen

Permalink to "Für High-Traffic-Anwendungen"

Nutze Redis, aber:

  1. Verstehe die Locking-Einschränkungen
  2. Bau Schutzmaßnahmen ein (mehr dazu im nächsten Artikel)
  3. Konfiguriere Persistenz (mindestens RDB-Snapshots)

Der “beste” Session Handler hängt von deinen Rahmenbedingungen ab. Datei Sessions funktionieren für die meisten Anwendungen. Wenn du skalieren musst, sind Datenbank Sessions ein vernünftiger Mittelweg. Redis ist die richtige Wahl für High-Traffic-Apps—aber nur wenn du das Locking-Problem handhabst.

Als nächstes: Redis Session Locking Fallstricke—die Race Conditions, die jeden überraschen.