[object Object]
High-Speed Redis Cache Patterns für Discord Bots: Sub-Millisekunden Leaderboards
Wenn ein Discord-Bot auf Tausenden von Servern aktiv ist, führt jede Chat-Nachricht (XP-Vergabe, Level-Aufstiege, Cooldown-Prüfungen) zu Datenbank-Interaktionen. Würde jede Nachricht einen SQL-Befehl auf PostgreSQL absetzen (SELECT * FROM users WHERE id = ?), bricht der Datenbank-Verbindungspool bei hoher Aktivität schnell zusammen.
Mit Redis als In-Memory-Caching-Layer lagern wir transiente Zustände in den RAM aus. Abfragen laufen in unter 1 Millisekunde ab und entlasten die persistente SQL-Datenbank um bis zu 95 %.
1. Die Caching-Topologie: Write-Through vs. Write-Behind
┌─────────────────────────────────────────────────────────────────┐
│ Discord Gateway Events │
└────────────────────────────────┬────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ KiroCloud Bot Engine │
├────────────────────────────────┬────────────────────────────────┤
│ 1. Schnelle In-Memory Abfrage │ 2. Asynchrone Batch-Speicherung│
│ (Latenz: 0.4 ms) │ (alle 60 Sekunden) │
│ ▼ │ ▼ │
│ ┌───────────────────────┐ │ ┌──────────────────────────┐ │
│ │ Redis Cache │ │ │ PostgreSQL Datenbank │ │
│ │ (Sorted Sets, TTL) │ │ │ (Persistente Sicherung) │ │
│ └───────────────────────┘ │ └──────────────────────────┘ │
└────────────────────────────────┴────────────────────────────────┘
2. Echtzeit-Ranglisten mit Redis Sorted Sets (ZSET)
Der naheliegendste Anwendungsfall für Redis in Discord-Bots sind Level- und Coin-Ranglisten (/leaderboard). In SQL erfordert dies ein teures ORDER BY xp DESC LIMIT 10, das bei Tabellen mit Millionen Zeilen erhebliche CPU-Zeit beansprucht.
In Redis nutzen wir Sorted Sets (ZADD, ZREVRANGE, ZREVRANK), die intern als Skip-Lists organisiert sind und Rangabfragen in $O(\log N)$ Komplexität lösen:
import Redis from 'ioredis';
const redis = new Redis(process.env.REDIS_URL || 'redis://localhost:6379');
/**
* Erhöht die Erfahrungspunkte eines Nutzers auf einem Server
*/
export async function addGuildUserXp(guildId: string, userId: string, xpDelta: number): Promise<number> {
const leaderboardKey = `guild:${guildId}:xp_leaderboard`;
// ZINCRBY erhöht den Score atomar und liefert den neuen Wert zurück
const newXp = await redis.zincrby(leaderboardKey, xpDelta, userId);
return parseFloat(newXp);
}
/**
* Ermittelt die Top 10 Nutzer eines Servers in Sub-Millisekunden
*/
export async function getTopUsers(guildId: string, limit = 10): Promise<Array<{ userId: string; score: number }>> {
const leaderboardKey = `guild:${guildId}:xp_leaderboard`;
// ZREVRANGE mit WITHSCORES liefert die ranghöchsten Einträge
const rawResults = await redis.zrevrange(leaderboardKey, 0, limit - 1, 'WITHSCORES');
const formatted: Array<{ userId: string; score: number }> = [];
for (let i = 0; i < rawResults.length; i += 2) {
formatted.push({
userId: rawResults[i],
score: parseFloat(rawResults[i + 1]),
});
}
return formatted;
}
/**
* Ermittelt den genauen Platz (Rank) eines bestimmten Nutzers
*/
export async function getUserRank(guildId: string, userId: string): Promise<number | null> {
const leaderboardKey = `guild:${guildId}:xp_leaderboard`;
// ZREVRANK liefert den 0-basierten Index absteigend sortiert
const rank = await redis.zrevrank(leaderboardKey, userId);
return rank !== null ? rank + 1 : null;
}
3. Cooldown-Verwaltung mit automatischer TTL-Verfallszeit
Befehls-Cooldowns (z. B. 5 Sekunden Pause zwischen /daily) erfordern keine persistente Speicherung. Mit Redis-TTL verfällt der Schlüssel automatisch:
export async function checkRateLimit(userId: string, action: string, windowSeconds: number): Promise<{ allowed: boolean; remainingSec: number }> {
const key = `ratelimit:${userId}:${action}`;
// SET mit NX (nur setzen wenn nicht existiert) und EX (Ablaufzeit)
const acquired = await redis.set(key, '1', 'EX', windowSeconds, 'NX');
if (acquired === 'OK') {
return { allowed: true, remainingSec: 0 };
}
const ttl = await redis.ttl(key);
return { allowed: false, remainingSec: Math.max(0, ttl) };
}
4. Cache-Invalidierung & PostgreSQL-Synchronisation
Um Datenverlust bei Server-Neustarts zu verhindern, synchronisieren wir geänderte Redis-Scores periodisch in die relationale PostgreSQL-Datenbank (Write-Behind-Pattern). Dies geschieht im Hintergrund alle 60 Sekunden ohne Blockierung des Discord-Event-Loops.
Luca Lichtenwald
Spezialisiert auf hochverfügbare Discord-Bot-Infrastrukturen, V8-Memory-Tuning und kryptografische Zero-Trust-Architekturen.