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
# 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.