Memcached ist ein verteiltes In-Memory Key-Value Cache-System. Es beschleunigt Webanwendungen durch Zwischenspeicherung von Datenbankabfragen und anderen Daten im Arbeitsspeicher.

Memcached vs Redis

| Feature | Memcached | Redis | |---------|-----------|-------| | Datenstrukturen | Nur Key-Value | Vielfältig | | Threading | Multi-Threaded | Single-Threaded | | Persistenz | Nein | Optional | | Replikation | Nein | Ja | | Memory-Effizienz | Besser | Gut | | Einfachheit | Sehr einfach | Komplexer |

Installation

Debian/Ubuntu

apt install memcached libmemcached-tools

CentOS/RHEL

dnf install memcached

Docker

docker run -d --name memcached \
    -p 11211:11211 \
    memcached:alpine -m 256

Service starten

systemctl enable memcached
systemctl start memcached

Konfiguration

/etc/memcached.conf

# /etc/memcached.conf

# Speichergröße in MB
-m 256

# Port
-p 11211

# Netzwerk-Interface
-l 127.0.0.1

# Benutzer
-u memcache

# Verbindungen
-c 1024

# Threads
-t 4

# Verbose Logging (zum Debuggen)
# -vv

# Maximale Objektgröße (Default 1MB)
-I 5m

Netzwerk-Zugriff

# Nur localhost (Standard, sicher)
-l 127.0.0.1

# Mehrere IPs
-l 127.0.0.1,192.168.1.10

# Alle Interfaces (unsicher!)
-l 0.0.0.0

Nach Änderungen

systemctl restart memcached

Grundlegende Befehle

Verbinden

# Telnet
telnet localhost 11211

# Netcat
nc localhost 11211

Daten speichern

# set key flags exptime bytes
set mykey 0 3600 5
hello
STORED

# add (nur wenn nicht existiert)
add newkey 0 3600 5
world
STORED

# replace (nur wenn existiert)
replace mykey 0 3600 6
hello2
STORED

Daten abrufen

# get
get mykey
VALUE mykey 0 6
hello2
END

# Mehrere Keys
get key1 key2 key3

Daten löschen

delete mykey
DELETED

# Alle löschen
flush_all
OK

Statistiken

stats
stats items
stats slabs
stats settings

PHP-Integration

PHP-Extension installieren

# Debian/Ubuntu
apt install php-memcached

# Prüfen
php -m | grep memcached

Basis-Verwendung

<?php
$memcached = new Memcached();
$memcached->addServer('localhost', 11211);

// Speichern
$memcached->set('user:1', ['name' => 'Max', 'email' => 'max@example.de'], 3600);

// Abrufen
$user = $memcached->get('user:1');

// Prüfen ob gefunden
if ($memcached->getResultCode() === Memcached::RES_NOTFOUND) {
    echo "Nicht im Cache";
}

// Löschen
$memcached->delete('user:1');
?>

Cache-Aside Pattern

<?php
class UserRepository {
    private $memcached;
    private $db;

    public function __construct() {
        $this->memcached = new Memcached();
        $this->memcached->addServer('localhost', 11211);
        $this->db = new PDO('mysql:host=localhost;dbname=app', 'user', 'pass');
    }

    public function getUser($id) {
        $cacheKey = "user:$id";

        // Versuche aus Cache
        $user = $this->memcached->get($cacheKey);

        if ($user !== false) {
            return $user;
        }

        // Aus Datenbank laden
        $stmt = $this->db->prepare('SELECT * FROM users WHERE id = ?');
        $stmt->execute([$id]);
        $user = $stmt->fetch(PDO::FETCH_ASSOC);

        if ($user) {
            // In Cache speichern (1 Stunde)
            $this->memcached->set($cacheKey, $user, 3600);
        }

        return $user;
    }

    public function updateUser($id, $data) {
        // Datenbank aktualisieren
        $stmt = $this->db->prepare('UPDATE users SET name = ?, email = ? WHERE id = ?');
        $stmt->execute([$data['name'], $data['email'], $id]);

        // Cache invalidieren
        $this->memcached->delete("user:$id");
    }
}
?>

Session-Storage

<?php
// php.ini
// session.save_handler = memcached
// session.save_path = "localhost:11211"

// Oder zur Laufzeit
ini_set('session.save_handler', 'memcached');
ini_set('session.save_path', 'localhost:11211');

session_start();
$_SESSION['user_id'] = 123;
?>

Python-Integration

Installation

pip install pymemcache

Verwendung

from pymemcache.client import base
import json

# Verbinden
client = base.Client(('localhost', 11211))

# JSON-Serialisierung
def json_serializer(key, value):
    if type(value) == str:
        return value, 1
    return json.dumps(value), 2

def json_deserializer(key, value, flags):
    if flags == 1:
        return value.decode('utf-8')
    if flags == 2:
        return json.loads(value.decode('utf-8'))
    raise Exception("Unknown flags")

client = base.Client(
    ('localhost', 11211),
    serializer=json_serializer,
    deserializer=json_deserializer
)

# Speichern
client.set('user:1', {'name': 'Max', 'age': 30}, expire=3600)

# Abrufen
user = client.get('user:1')
print(user)

# Löschen
client.delete('user:1')

Node.js-Integration

Installation

npm install memcached

Verwendung

const Memcached = require('memcached');

const memcached = new Memcached('localhost:11211');

// Speichern
memcached.set('key', 'value', 3600, (err) => {
    if (err) console.error(err);
    console.log('Gespeichert');
});

// Abrufen
memcached.get('key', (err, data) => {
    if (err) console.error(err);
    console.log(data);
});

// Mit Promises
const util = require('util');
const getAsync = util.promisify(memcached.get).bind(memcached);
const setAsync = util.promisify(memcached.set).bind(memcached);

async function example() {
    await setAsync('user:1', { name: 'Max' }, 3600);
    const user = await getAsync('user:1');
    console.log(user);
}

Verteilter Cache

Mehrere Server

<?php
$memcached = new Memcached();
$memcached->addServers([
    ['server1.example.de', 11211, 33],  // Weight 33%
    ['server2.example.de', 11211, 33],
    ['server3.example.de', 11211, 34]
]);

// Consistent Hashing
$memcached->setOption(Memcached::OPT_DISTRIBUTION, Memcached::DISTRIBUTION_CONSISTENT);
$memcached->setOption(Memcached::OPT_LIBKETAMA_COMPATIBLE, true);
?>

Failover

<?php
$memcached = new Memcached();
$memcached->setOptions([
    Memcached::OPT_CONNECT_TIMEOUT => 100,
    Memcached::OPT_RETRY_TIMEOUT => 1,
    Memcached::OPT_DEAD_TIMEOUT => 10,
    Memcached::OPT_REMOVE_FAILED_SERVERS => true
]);
?>

Monitoring

memcached-tool

# Statistiken
memcached-tool localhost:11211 stats

# Slab-Info
memcached-tool localhost:11211 display

# Dump Keys
memcached-tool localhost:11211 dump

Wichtige Metriken

# Via telnet
echo "stats" | nc localhost 11211

# Wichtige Werte
# get_hits / (get_hits + get_misses) = Hit Ratio
# bytes / limit_maxbytes = Memory Usage
# curr_connections = Aktive Verbindungen

Prometheus

# memcached_exporter
docker run -d -p 9150:9150 \
    prom/memcached-exporter \
    --memcached.address=memcached:11211

Cache-Strategien

Key-Naming

<?php
// Struktur: prefix:entity:id:version
$key = "app:user:123:v1";

// Mit Namespace für Invalidierung
$namespace = $memcached->get('user_namespace') ?: 1;
$key = "user:{$namespace}:123";

// Alle User-Cache invalidieren
$memcached->increment('user_namespace');
?>

Cache Stampede Prevention

<?php
function getWithLock($key, $callback, $ttl = 3600) {
    global $memcached;

    $value = $memcached->get($key);
    if ($value !== false) {
        return $value;
    }

    // Lock setzen
    $lockKey = "lock:$key";
    if (!$memcached->add($lockKey, 1, 10)) {
        // Warten und erneut versuchen
        usleep(50000);
        return getWithLock($key, $callback, $ttl);
    }

    // Wert berechnen
    $value = $callback();

    // Speichern und Lock freigeben
    $memcached->set($key, $value, $ttl);
    $memcached->delete($lockKey);

    return $value;
}
?>

Sicherheit

Netzwerk absichern

# Nur localhost
-l 127.0.0.1

# Mit Firewall
ufw allow from 192.168.1.0/24 to any port 11211

SASL-Authentifizierung

# memcached.conf
-S  # SASL aktivieren

# /etc/sasl2/memcached.conf
mech_list: plain
sasldb_path: /etc/memcached-sasldb2
# Benutzer erstellen
saslpasswd2 -a memcached -c -f /etc/memcached-sasldb2 username
<?php
$memcached = new Memcached();
$memcached->setOption(Memcached::OPT_BINARY_PROTOCOL, true);
$memcached->setSaslAuthData('username', 'password');
$memcached->addServer('localhost', 11211);
?>

Troubleshooting

Häufige Probleme

# Verbindung fehlgeschlagen
# → Service läuft?
systemctl status memcached

# → Port offen?
netstat -tlnp | grep 11211

# Memory voll
# → Stats prüfen
echo "stats" | nc localhost 11211 | grep bytes

# Evictions hoch
# → Mehr Memory oder kürzere TTL

Debug-Modus

# Verbose starten
memcached -vv -m 64 -p 11211

Zusammenfassung

| Befehl | Funktion | |--------|----------| | set | Wert speichern | | get | Wert abrufen | | delete | Wert löschen | | flush_all | Alles löschen | | stats | Statistiken |

| Parameter | Beschreibung | |-----------|--------------| | -m | Speicher in MB | | -p | Port | | -l | Listen-Adresse | | -c | Max. Verbindungen | | -t | Threads |

| Datei | Funktion | |-------|----------| | /etc/memcached.conf | Konfiguration | | /var/run/memcached/ | Runtime-Daten |

Fazit

Memcached ist ideal für einfaches, schnelles Caching. Die Multi-Threading-Architektur skaliert gut auf mehrere CPU-Kerne. Für komplexe Datenstrukturen oder Persistenz ist Redis besser geeignet. Die Integration in PHP, Python und Node.js ist unkompliziert. Für verteilte Setups sorgt Consistent Hashing für gleichmäßige Lastverteilung. Memcached bleibt die erste Wahl für reines Session- und Query-Caching.