Memcached und Redis sind die beiden führenden In-Memory-Datenspeicher für Caching. Beide beschleunigen Anwendungen erheblich, haben aber unterschiedliche Stärken und Einsatzgebiete.

Grundlegender Vergleich

Übersicht

| Merkmal | Memcached | Redis | |---------|-----------|-------| | Datenstrukturen | Key-Value | Vielfältig | | Persistenz | Nein | Ja (optional) | | Replikation | Nein | Ja | | Clustering | Client-seitig | Native | | Speicherverwaltung | Slab Allocator | Jemalloc | | Threading | Multi-threaded | Single-threaded | | Pub/Sub | Nein | Ja |

Wann Memcached?

- Einfaches Key-Value-Caching
- Horizontale Skalierung über mehrere Server
- Maximale Einfachheit gewünscht
- Session-Storage ohne Persistenz
- Multi-threaded Performance nötig

Wann Redis?

- Komplexe Datenstrukturen (Listen, Sets, Hashes)
- Persistenz erforderlich
- Pub/Sub-Messaging
- Atomare Operationen
- Lua-Scripting
- Replikation und Clustering

Memcached Installation

Debian/Ubuntu

apt install memcached libmemcached-tools

systemctl enable memcached
systemctl start memcached

CentOS/RHEL

dnf install memcached

systemctl enable memcached
systemctl start memcached

Konfiguration

# /etc/memcached.conf

# Port
-p 11211

# Speicher (MB)
-m 512

# Maximale Verbindungen
-c 1024

# Benutzer
-u memcache

# Listen-Adresse
-l 127.0.0.1

# Threads
-t 4

# Maximale Item-Größe
-I 2m

Verbindungstest

# Telnet
telnet localhost 11211
stats
quit

# Mit memcstat
memcstat --servers=localhost

Redis Installation

Debian/Ubuntu

apt install redis-server

systemctl enable redis-server
systemctl start redis-server

CentOS/RHEL

dnf install redis

systemctl enable redis
systemctl start redis

Konfiguration

# /etc/redis/redis.conf

# Bind
bind 127.0.0.1

# Port
port 6379

# Passwort
requirepass sicheres_passwort

# Speicherlimit
maxmemory 512mb
maxmemory-policy allkeys-lru

# Persistenz (optional)
save 900 1
save 300 10
save 60 10000

# AOF (Append Only File)
appendonly yes
appendfsync everysec

Verbindungstest

redis-cli
> AUTH sicheres_passwort
> PING
PONG
> INFO

Datenstrukturen

Memcached (nur Strings)

# Telnet-Befehle
set key 0 3600 5
value
STORED

get key
VALUE key 0 5
value
END

delete key
DELETED

Redis (vielfältig)

# Strings
SET user:1 "John"
GET user:1

# Hashes
HSET user:1 name "John" email "john@example.com"
HGET user:1 name
HGETALL user:1

# Listen
LPUSH queue:jobs "job1"
RPUSH queue:jobs "job2"
LPOP queue:jobs

# Sets
SADD tags:article:1 "php" "redis" "cache"
SMEMBERS tags:article:1
SINTER tags:article:1 tags:article:2

# Sorted Sets
ZADD leaderboard 100 "player1"
ZADD leaderboard 150 "player2"
ZRANGE leaderboard 0 -1 WITHSCORES

# HyperLogLog (Unique Counts)
PFADD visitors "user1" "user2" "user1"
PFCOUNT visitors

PHP-Integration

Memcached mit PHP

# Extension installieren
apt install php-memcached
systemctl restart php8.2-fpm
<?php
$memcached = new Memcached();
$memcached->addServer('127.0.0.1', 11211);

// Setzen
$memcached->set('key', 'value', 3600);

// Lesen
$value = $memcached->get('key');

// Löschen
$memcached->delete('key');

// Multi-Get
$keys = ['key1', 'key2', 'key3'];
$values = $memcached->getMulti($keys);

// Atomic Increment
$memcached->set('counter', 0);
$memcached->increment('counter');

Redis mit PHP

# Extension installieren
apt install php-redis
systemctl restart php8.2-fpm
<?php
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$redis->auth('sicheres_passwort');

// Strings
$redis->set('key', 'value');
$redis->setex('key', 3600, 'value'); // Mit TTL
$value = $redis->get('key');

// Hashes
$redis->hSet('user:1', 'name', 'John');
$redis->hSet('user:1', 'email', 'john@example.com');
$user = $redis->hGetAll('user:1');

// Listen
$redis->lPush('queue', 'job1', 'job2');
$job = $redis->rPop('queue');

// Sets
$redis->sAdd('tags', 'php', 'redis');
$tags = $redis->sMembers('tags');

// Sorted Sets
$redis->zAdd('leaderboard', 100, 'player1');
$top = $redis->zRevRange('leaderboard', 0, 9, true);

// Pipelines (Batching)
$pipe = $redis->multi(Redis::PIPELINE);
$pipe->set('key1', 'value1');
$pipe->set('key2', 'value2');
$pipe->get('key1');
$results = $pipe->exec();

Session-Handling

Memcached Sessions (PHP)

; /etc/php/8.2/fpm/conf.d/20-sessions.ini

session.save_handler = memcached
session.save_path = "127.0.0.1:11211"

Redis Sessions (PHP)

; /etc/php/8.2/fpm/conf.d/20-sessions.ini

session.save_handler = redis
session.save_path = "tcp://127.0.0.1:6379?auth=sicheres_passwort"

WordPress Object Cache

// Memcached: wp-content/object-cache.php
// Plugin: W3 Total Cache oder Memcached Object Cache

// Redis: wp-content/object-cache.php
// Plugin: Redis Object Cache

// wp-config.php
define('WP_REDIS_HOST', '127.0.0.1');
define('WP_REDIS_PORT', 6379);
define('WP_REDIS_PASSWORD', 'sicheres_passwort');

Caching-Patterns

Cache-Aside (Lazy Loading)

<?php
function getUser($id, $redis) {
    $key = "user:{$id}";

    // Versuche aus Cache
    $cached = $redis->get($key);
    if ($cached !== false) {
        return json_decode($cached, true);
    }

    // Aus Datenbank laden
    $user = loadUserFromDatabase($id);

    // Im Cache speichern
    $redis->setex($key, 3600, json_encode($user));

    return $user;
}

Write-Through

<?php
function updateUser($id, $data, $redis, $db) {
    // In Datenbank schreiben
    $db->update('users', $data, ['id' => $id]);

    // Cache aktualisieren
    $key = "user:{$id}";
    $redis->setex($key, 3600, json_encode($data));
}

Cache Invalidation

<?php
// Einzelnen Key löschen
$redis->del("user:{$id}");

// Pattern löschen (mit SCAN)
$iterator = null;
while ($keys = $redis->scan($iterator, ['match' => 'user:*', 'count' => 100])) {
    foreach ($keys as $key) {
        $redis->del($key);
    }
}

// Tags-basierte Invalidierung
$redis->sAdd('cache:tags:users', "user:1", "user:2");
$keys = $redis->sMembers('cache:tags:users');
$redis->del($keys);

Performance-Vergleich

Benchmarking

# Redis Benchmark
redis-benchmark -h 127.0.0.1 -p 6379 -n 100000 -c 50 -q

# Memcached mit memtier_benchmark
apt install memtier-benchmark
memtier_benchmark -s 127.0.0.1 -p 11211 -P memcache_binary -n 100000 -c 50

Typische Ergebnisse

| Operation | Memcached | Redis | |-----------|-----------|-------| | SET | ~180.000/s | ~150.000/s | | GET | ~200.000/s | ~180.000/s | | INCR | ~180.000/s | ~160.000/s |

Speichereffizienz

# Memcached Stats
memcstat --servers=localhost

# Redis Info
redis-cli INFO memory

Hochverfügbarkeit

Memcached (Client-seitig)

<?php
// Consistent Hashing über mehrere Server
$memcached = new Memcached();
$memcached->setOption(Memcached::OPT_DISTRIBUTION, Memcached::DISTRIBUTION_CONSISTENT);
$memcached->addServers([
    ['server1', 11211],
    ['server2', 11211],
    ['server3', 11211],
]);

Redis Sentinel

# /etc/redis/sentinel.conf

port 26379
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 60000
sentinel parallel-syncs mymaster 1
redis-sentinel /etc/redis/sentinel.conf

Redis Cluster

# Cluster erstellen
redis-cli --cluster create \
    192.168.1.1:6379 192.168.1.2:6379 192.168.1.3:6379 \
    192.168.1.4:6379 192.168.1.5:6379 192.168.1.6:6379 \
    --cluster-replicas 1

Monitoring

Memcached

# Statistiken
echo "stats" | nc localhost 11211

# Wichtige Metriken
memcstat --servers=localhost | grep -E "curr_items|bytes|get_hits|get_misses"

# Hit-Rate berechnen
# hit_rate = get_hits / (get_hits + get_misses)

Redis

# Allgemeine Info
redis-cli INFO

# Memory
redis-cli INFO memory

# Stats
redis-cli INFO stats

# Langsame Queries
redis-cli SLOWLOG get 10

# Echtzeit-Monitor
redis-cli MONITOR

Prometheus/Grafana

# Redis Exporter
docker run -d --name redis_exporter \
    -p 9121:9121 \
    oliver006/redis_exporter \
    --redis.addr redis://localhost:6379

Sicherheit

Memcached absichern

# /etc/memcached.conf

# Nur localhost
-l 127.0.0.1

# SASL-Auth aktivieren
-S

Redis absichern

# /etc/redis/redis.conf

# Passwort
requirepass sicheres_passwort

# Nur localhost
bind 127.0.0.1

# Protected Mode
protected-mode yes

# Gefährliche Befehle umbenennen
rename-command FLUSHALL ""
rename-command FLUSHDB ""
rename-command DEBUG ""
rename-command CONFIG "ADMIN_CONFIG"

Firewall

# UFW
ufw deny 11211
ufw deny 6379

# Nur bei Bedarf öffnen (intern)
ufw allow from 192.168.1.0/24 to any port 6379

Anwendungsfälle

Session-Caching

Empfehlung: Redis
- Persistenz möglich
- Automatisches Expire
- Bessere Datentypen

Object Cache (WordPress)

Empfehlung: Redis
- Unterstützt komplexe Objekte
- Bessere Plugin-Unterstützung

Page Cache

Empfehlung: Beide geeignet
- Einfache Key-Value-Speicherung
- Memcached bei sehr hohem Durchsatz

Leaderboards/Rankings

Empfehlung: Redis
- Sorted Sets ideal für Rankings
- ZADD, ZRANK, ZRANGE

Message Queue

Empfehlung: Redis
- LPUSH/BRPOP für Queues
- Pub/Sub für Real-time

Rate Limiting

Empfehlung: Redis
- INCR mit EXPIRE
- Atomare Operationen

Zusammenfassung

| Kriterium | Memcached | Redis | |-----------|-----------|-------| | Einfachheit | ★★★★★ | ★★★☆☆ | | Datenstrukturen | ★☆☆☆☆ | ★★★★★ | | Persistenz | ☆☆☆☆☆ | ★★★★★ | | Skalierung | ★★★★☆ | ★★★★★ | | Speichereffizienz | ★★★★☆ | ★★★☆☆ | | Multi-Threading | ★★★★★ | ★★☆☆☆ |

| Anwendungsfall | Empfehlung | |----------------|------------| | Einfaches Caching | Beide | | Sessions | Redis | | Object Cache | Redis | | Message Queue | Redis | | Analytics | Redis | | Nur Cache, keine Persistenz | Memcached |

Fazit

Für die meisten Anwendungsfälle ist Redis die bessere Wahl aufgrund seiner vielfältigen Datenstrukturen, Persistenzoptionen und Features wie Pub/Sub. Memcached eignet sich für einfaches Caching mit höchsten Performance-Anforderungen und Multi-Threading. Beide können auch kombiniert werden: Memcached für Session-Caching und Redis für komplexere Datenstrukturen.