---
title: "Shopware 6 & Redis: Wenn falsche Konfiguration mehr schadet als nutzt"
description: "Häufige Redis-Fehlkonfigurationen in Shopware 6, die Performance kosten statt sie zu verbessern – von Persistenz auf Cache-Instanzen bis fehlenden Eviction-Policies – und wie man sie behebt."
canonical_url: "https://kickbyte.de/de/blog/shopware-6-redis-fehlkonfiguration"
last_updated: "2026-08-27T02:38:26.634Z"
---

# Shopware 6 & Redis: Wenn falsche Konfiguration mehr schadet als nutzt

Redis ist in [Shopware 6](/leistungen/ecommerce-entwicklung) das Mittel der Wahl, um Cache, Sessions und HTTP-Cache aus der Datenbank bzw. dem Dateisystem zu
holen und die Performance drastisch zu steigern. In der Praxis sehen wir aber regelmäßig Setups, in denen Redis zum
Flaschenhals wird — nicht, weil Redis langsam ist, sondern weil es falsch konfiguriert wurde.

Dieser Artikel zeigt die häufigsten Fehler und wie man sie vermeidet.

<youtube-video className="not-prose" title="Shopware 6 & Redis: Wenn falsche Konfiguration mehr schadet als nutzt" video-id="JThcPGsfQgo">



</youtube-video>

---

## Fehler 1: Eine Redis-Instanz für alles

Das ist der Klassiker. Eine einzige Redis-Instanz auf Port 6379 wird für Cache, Sessions, HTTP-Cache, Cart-Persistenz,
Number-Ranges, Locks, Increment-Storage und Message-Queue verwendet.

**Warum das schadet:**

- Cache-Daten konkurrieren mit Session-Daten um den verfügbaren Speicher. Wenn `maxmemory` greift und die
Eviction-Policy Keys räumt, können das Session- oder Warenkorb-Keys sein — Kunden fliegen aus dem Checkout oder
verlieren ihren Warenkorb.
- Ein unbedachtes `FLUSHALL` im Debugging löscht alles auf einen Schlag — Sessions, Warenkörbe, Number-Range-States.
- Unterschiedliche Datentypen brauchen unterschiedliche Persistenz- und Eviction-Strategien. Cache-Daten sind flüchtig,
Warenkörbe und Number-Ranges sind es nicht.

**Lösung: Mindestens zwei getrennte Redis-Instanzen — ephemeral und persistent.**

Seit Shopware 6.6.8.0 werden benannte Redis-Connections direkt in der `shopware.yaml` unterstützt:

```yaml
# config/packages/shopware.yaml
shopware:
    redis:
        connections:
            ephemeral:
                dsn: '%env(REDIS_EPHEMERAL)%'
            persistent:
                dsn: '%env(REDIS_PERSISTENT)%'
```

```dotenv
REDIS_EPHEMERAL=redis://redis-cache:6379
REDIS_PERSISTENT=redis://redis-session:6380
```

Diese Connection-Namen können dann in allen Subsystemen referenziert werden — Cache, Sessions, Cart, Number-Ranges,
Locks, Increment-Storage.

<table>
<thead>
  <tr>
    <th>
      Instanz
    </th>
    
    <th>
      Zweck
    </th>
    
    <th>
      Persistenz
    </th>
    
    <th>
      Eviction-Policy
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        ephemeral
      </code>
    </td>
    
    <td>
      App-Cache, HTTP-Cache, Locks, Increment
    </td>
    
    <td>
      <strong>
        Aus
      </strong>
    </td>
    
    <td>
      <code>
        allkeys-lru
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        persistent
      </code>
    </td>
    
    <td>
      Sessions, Warenkörbe, Number-Ranges
    </td>
    
    <td>
      <strong>
        An
      </strong>
      
       (RDB + AOF)
    </td>
    
    <td>
      <code>
        volatile-lru
      </code>
    </td>
  </tr>
</tbody>
</table>

Docker-Compose-Auszug:

```yaml
services:
    redis-cache:
        image: redis:7-alpine
        command: >
            redis-server
              --maxmemory 2gb
              --maxmemory-policy allkeys-lru
              --save ""
              --appendonly no
        volumes: [] # Kein Volume — Cache ist flüchtig

    redis-session:
        image: redis:7-alpine
        command: >
            redis-server
              --maxmemory 256mb
              --maxmemory-policy volatile-lru
              --appendonly yes
              --appendfsync everysec
              --save "3600 1" --save "300 100"
        volumes:
            - redis-session-data:/data
```

Die Shopware-Docs empfehlen `volatile-lru` für die persistente Instanz — damit werden nur Keys mit gesetztem TTL
evictet, während Keys ohne Ablauf (z. B. Number-Ranges) immer erhalten bleiben. Das ist der entscheidende Unterschied zu
`allkeys-lru`, das auch Keys ohne TTL löschen würde.

---

## Fehler 2: Persistenz auf der Cache-Instanz aktiviert

Standardmäßig startet Redis mit aktivierten RDB-Snapshots (`save 3600 1 300 100 60 10000`). Das bedeutet: Redis schreibt
regelmäßig den gesamten Datensatz auf die Festplatte, auch wenn es sich nur um flüchtigen Cache handelt.

**Warum das schadet:**

- Der `BGSAVE`-Prozess forkt den Redis-Prozess. Bei 2 GB Cache-Daten werden kurzzeitig 2 GB zusätzlicher RAM durch
Copy-on-Write belegt. Auf einer VM mit 4 GB RAM kann das zum OOM-Kill führen.
- Disk-I/O durch den Dump belastet das System unnötig, besonders auf Shared-Hosting oder kleinen VPS mit langsamen
Platten.
- Beim Neustart lädt Redis den Dump und stellt veraltete Cache-Daten wieder her. Das klingt erstmal gut, ist aber
kontraproduktiv: Der Cache enthält nach einem Deployment möglicherweise veraltete Einträge, die Shopware dann
ausliefert, statt sie neu zu generieren.

**Lösung:**

```conf
# redis-cache.conf (ephemeral)
save ""
appendonly no
```

Zwei Zeilen. Kein RDB, kein AOF. Die Cache-Instanz startet leer — genau so soll es sein. Shopware baut den Cache bei
Bedarf neu auf.

Für die **persistente Instanz** (Sessions, Carts, Number-Ranges) ist Persistenz dagegen **zwingend**:

```conf
# redis-session.conf (persistent)
appendonly yes
appendfsync everysec
save 3600 1
save 300 100
```

---

## Fehler 3: Kein maxmemory gesetzt

Ohne `maxmemory`-Limit wächst Redis so lange, bis der Arbeitsspeicher des Hosts voll ist. Der Linux-OOM-Killer beendet
dann den Redis-Prozess — oder schlimmer: den MySQL-Prozess, weil der mehr RAM belegt.

**Warum das schadet:**

- Unkontrollierter Speicherverbrauch führt zu Systeminstabilität.
- Swap-Nutzung macht Redis um Größenordnungen langsamer. Redis und Swap vertragen sich nicht — eine Redis-Instanz, die
swappt, ist langsamer als ein Cache, der direkt auf MariaDB liegt.

**Lösung:**

Faustregel für einen typischen Shopware-Shop:

- **Cache-Instanz (ephemeral):** 1–2 GB (je nach Produktanzahl und Kategorientiefe)
- **Persistent-Instanz:** 128–256 MB (abhängig von gleichzeitigen Nutzern und Warenkörben)

Immer explizit setzen:

```conf
maxmemory 2gb
maxmemory-policy allkeys-lru  # für ephemeral
```

Und überwachen. Ein `INFO memory`-Check gehört ins Monitoring:

```bash
redis-cli -p 6379 INFO memory | grep used_memory_human
redis-cli -p 6379 INFO memory | grep maxmemory_human
```

---

## Fehler 4: Falsche Eviction-Policy

Redis bietet verschiedene Eviction-Policies. Die Wahl hat direkten Einfluss auf das Verhalten des Shops.

**Häufige Fehlkonfiguration:**

- `noeviction` auf der Cache-Instanz → Redis lehnt Schreiboperationen ab, sobald `maxmemory` erreicht ist. Shopware
wirft Fehler, der Shop steht.
- `allkeys-random` statt `allkeys-lru` → Redis löscht zufällige Keys statt des am längsten nicht genutzten. Häufig
angefragte Cache-Einträge werden gelöscht, selten gebrauchte bleiben.
- `allkeys-lru` auf der persistenten Instanz → Redis kann Number-Range-Keys löschen, die kein TTL haben. Das führt zu
doppelt vergebenen Bestellnummern.

**Die richtige Zuordnung (laut Shopware-Docs):**

<table>
<thead>
  <tr>
    <th>
      Instanz
    </th>
    
    <th>
      Policy
    </th>
    
    <th>
      Warum
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      Ephemeral (Cache)
    </td>
    
    <td>
      <code>
        allkeys-lru
      </code>
    </td>
    
    <td>
      Älteste Einträge fliegen zuerst, neue werden immer geschrieben
    </td>
  </tr>
  
  <tr>
    <td>
      Persistent (Sessions, Carts, Number-Ranges)
    </td>
    
    <td>
      <code>
        volatile-lru
      </code>
    </td>
    
    <td>
      Nur Keys mit TTL werden evictet; Keys ohne TTL (Number-Ranges) bleiben erhalten
    </td>
  </tr>
</tbody>
</table>

Der Unterschied ist subtil, aber kritisch: `volatile-lru` schützt alle Keys ohne explizites TTL vor dem Löschen.
Sessions und Warenkörbe haben TTLs und können bei Speicherdruck evictet werden. Number-Ranges haben kein TTL und bleiben
sicher.

---

## Fehler 5: Shopware-seitige Konfiguration unvollständig

Redis läuft, die Verbindung steht — aber Shopware nutzt es nicht für alle Subsysteme, weil die Konfiguration nur
teilweise gemacht wurde.

**Typische Fehler:**

- Nur den App-Cache auf Redis, aber Session-Handler, Cart-Storage, Locks und Increment vergessen.
- Lock-DSN zeigt noch auf `flock` statt Redis → bei Horizontal-Scaling knallt es.
- Increment-Storage bleibt auf MySQL → unnötige DB-Last durch Locking-Queries.

Vollständige Konfiguration mit benannten Connections (≥ 6.6.8.0):

```yaml
# config/packages/shopware.yaml
shopware:
    redis:
        connections:
            ephemeral:
                dsn: '%env(REDIS_EPHEMERAL)%'
            persistent:
                dsn: '%env(REDIS_PERSISTENT)%'

    cache:
        invalidation:
            delay: 0
            count: 150
            delay_options:
                storage: redis
                connection: 'ephemeral'

    cart:
        redis_url: '%env(REDIS_PERSISTENT)%'

    number_range:
        redis_url: '%env(REDIS_PERSISTENT)%'

    increment:
        message_queue:
            type: redis
            config:
                url: '%env(REDIS_EPHEMERAL)%'
        user_activity:
            type: redis
            config:
                url: '%env(REDIS_EPHEMERAL)%'
```

```yaml
# config/packages/prod/framework.yaml
framework:
    cache:
        default_redis_provider: '%env(REDIS_EPHEMERAL)%'
        pools:
            cache.object:
                adapter: cache.adapter.redis
                tags: true
            cache.http:
                adapter: cache.adapter.redis
                tags: true

    session:
        handler_id: '%env(REDIS_PERSISTENT)%'

    lock:
        main: '%env(REDIS_EPHEMERAL)%'
```

Wer den Increment-Storage nicht braucht (keine Nutzung der Admin-Live-Statistiken), kann ihn auf `array` setzen und
spart sich die Last komplett:

```yaml
shopware:
    increment:
        message_queue:
            type: array
        user_activity:
            type: array
```

---

## Fehler 6: ?persistent=1 falsch verstanden

Ein Klassiker, den man in vielen Tutorials sieht:

```yaml
shopware:
    cart:
        redis_url: 'redis://redis:6380?persistent=1'
```

Der Parameter `?persistent=1` hat **nichts mit der Datenpersistenz** zu tun. Er aktiviert **Persistent Connections** —
also Connection Pooling auf PHP-Seite. Damit wird die TCP-Verbindung zu Redis über mehrere Requests hinweg offen
gehalten, statt sie bei jedem Request neu aufzubauen.

**Warum die Verwechslung schadet:**

- Entwickler setzen `?persistent=1` und glauben, damit sei die Datenpersistenz geregelt. Die eigentliche
Redis-Persistenz (RDB/AOF) wird nicht konfiguriert. Nach einem Redis-Neustart sind alle Warenkörbe weg.
- Umgekehrt: `?persistent=1` wird weggelassen, weil man keine Persistenz will (Cache-Instanz). Dabei hätte Connection
Pooling dort die beste Wirkung, weil Cache-Requests häufig sind.

**Lösung:**

- **Datenpersistenz** wird auf Redis-Server-Seite konfiguriert (`save`, `appendonly`)
- **Connection Pooling** wird in der DSN konfiguriert (`?persistent=1`) — sinnvoll für beide Instanzen

---

## Fehler 7: Cart-Migration vergessen

Shopware speichert Warenkörbe standardmäßig in MySQL. Der Umstieg auf Redis erfordert eine explizite Migration, sonst
sind bestehende Warenkörbe nach dem Config-Wechsel weg.

```bash
# Warenkörbe von MySQL zu Redis migrieren
bin/console cart:migrate
```

Das Kommando liest die Warenkörbe aus der Datenbank und schreibt sie in die konfigurierte Redis-Instanz. Es nutzt
automatisch die `redis_url` aus der `shopware.yaml`. Der Befehl ist idempotent und kann im Deployment-Script ausgeführt
werden.

Ohne diesen Schritt verlieren alle Kunden mit aktiven Warenkörben ihre Artikel nach dem Switch.

---

## Fehler 8: Kein Monitoring

Redis läuft still vor sich hin. Niemand merkt, dass die Hit-Rate bei 12 % liegt, der Speicher zu 98 % voll ist oder die
Latenz bei 50 ms statt 0,2 ms liegt.

**Mindest-Monitoring:**

```bash
# Hit-Rate prüfen
redis-cli -p 6379 INFO stats | grep -E "keyspace_hits|keyspace_misses"

# Auslastung
redis-cli -p 6379 INFO memory | grep used_memory_peak_human

# Verbundene Clients
redis-cli -p 6379 INFO clients | grep connected_clients

# Slow-Log
redis-cli -p 6379 SLOWLOG GET 10
```

Die Hit-Rate für die Cache-Instanz sollte über 80 % liegen. Liegt sie darunter, stimmt entweder das TTL nicht, die
Eviction-Policy ist falsch, oder `maxmemory` ist zu niedrig und Redis räumt ständig auf.

Für ein sauberes Setup: Prometheus mit dem `redis_exporter` und ein Grafana-Dashboard. Die Investition von 30 Minuten
Einrichtung spart Stunden Debugging.

---

## Fehler 9: TCP-Konfiguration ignoriert

Redis und Shopware laufen auf demselben Host, kommunizieren aber über TCP statt Unix-Socket. Oder Redis läuft auf einem
separaten Host, aber TCP-Keepalive und Timeout sind auf Default.

**Wenn Redis auf demselben Host läuft:**

Unix-Socket statt TCP spart den gesamten TCP-Stack-Overhead. Das sind bei hohem Durchsatz messbare Mikrosekunden pro
Request:

```conf
# redis-cache.conf
unixsocket /var/run/redis/redis-cache.sock
unixsocketperm 770
port 0
```

```dotenv
REDIS_EPHEMERAL=redis:///var/run/redis/redis-cache.sock
```

**Wenn Redis remote läuft:**

```conf
tcp-keepalive 60
timeout 300
tcp-backlog 511
```

Ohne `tcp-keepalive` können tote Verbindungen minutenlang offen bleiben und den Connection-Pool blockieren.

Bei Nutzung eines Redis-Clusters zusätzlich in der `php.ini` setzen:

```ini
redis.clusters.cache_slots=1
```

Das überspringt den Cluster-Node-Lookup bei jeder Verbindung.

---

## Zusammenfassung

<table>
<thead>
  <tr>
    <th>
      Fehler
    </th>
    
    <th>
      Auswirkung
    </th>
    
    <th>
      Fix
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      Eine Instanz für alles
    </td>
    
    <td>
      Eviction löscht Sessions/Carts
    </td>
    
    <td>
      Getrennte ephemeral/persistent Instanzen
    </td>
  </tr>
  
  <tr>
    <td>
      Persistenz auf Cache
    </td>
    
    <td>
      OOM-Kill, veralteter Cache nach Restart
    </td>
    
    <td>
      <code>
        save ""
      </code>
      
      , <code>
        appendonly no
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Kein <code>
        maxmemory
      </code>
    </td>
    
    <td>
      OOM-Kill, Swap-Nutzung
    </td>
    
    <td>
      Explizites Limit setzen
    </td>
  </tr>
  
  <tr>
    <td>
      Falsche Eviction-Policy
    </td>
    
    <td>
      Shop-Fehler oder Datenverlust
    </td>
    
    <td>
      <code>
        allkeys-lru
      </code>
      
       (Cache), <code>
        volatile-lru
      </code>
      
       (persistent)
    </td>
  </tr>
  
  <tr>
    <td>
      Unvollständige Shopware-Config
    </td>
    
    <td>
      Redis läuft, wird aber nicht genutzt
    </td>
    
    <td>
      Alle Subsysteme konfigurieren
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        ?persistent=1
      </code>
      
       verwechselt
    </td>
    
    <td>
      Keine Datenpersistenz trotz Annahme
    </td>
    
    <td>
      Server-seitige Persistenz konfigurieren
    </td>
  </tr>
  
  <tr>
    <td>
      Cart-Migration vergessen
    </td>
    
    <td>
      Warenkörbe weg nach Redis-Umstieg
    </td>
    
    <td>
      <code>
        bin/console cart:migrate
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Kein Monitoring
    </td>
    
    <td>
      Probleme bleiben unentdeckt
    </td>
    
    <td>
      Hit-Rate, Memory, Slow-Log überwachen
    </td>
  </tr>
  
  <tr>
    <td>
      TCP statt Socket
    </td>
    
    <td>
      Unnötiger Overhead
    </td>
    
    <td>
      Unix-Socket bei Co-Location
    </td>
  </tr>
</tbody>
</table>

Redis ist ein mächtiges Werkzeug — aber nur, wenn die Konfiguration zum Use-Case passt. Ein falsch konfiguriertes Redis
ist schlimmer als kein Redis, weil es eine zusätzliche Fehlerquelle einführt und dabei den Eindruck erweckt, die
Performance-Arbeit sei bereits erledigt.
