Caching in TypeScript: Redis, Memcached und integrierte Lösungen (Fortgeschrittenenniveau)

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Warum fortgeschrittenes Caching für TypeScript-Anwendungen notwendig ist



In modernen hochbelasteten Systemen ist Caching keine Option mehr – es ist ein obligatorisches Architekturelement. Für TypeScript-Entwickler, die mit Node.js arbeiten, kann die Wahl der richtigen Caching-Strategie die API-Antwortzeit von 200 ms auf 2 ms reduzieren und die Datenbanklast um 80-90 % senken. In diesem Artikel betrachten wir drei Ansätze für Caching auf fortgeschrittenem Niveau: Redis, Memcached und integrierte Lösungen in TypeScript.



1. Integriertes Caching in TypeScript: Von einfacher Map bis zum LRU-Cache



Integrierte Lösungen sind ideal für monolithische Anwendungen oder Microservices, bei denen kein verteilter Cache erforderlich ist. Die einfachste Variante ist die Verwendung von Map, aber für die Produktion ist sie aufgrund fehlender TTL und Speicherkontrolle ungeeignet.



Implementierung eines LRU-Cache mit TTL



Betrachten wir eine fortgeschrittene Implementierung eines LRU (Least Recently Used) Cache mit automatischer Bereinigung:



interface CacheEntry<T> {  value: T;  expiresAt: number;}

class LRUCache<T> { private cache: Map<string, CacheEntry<T>>; private readonly maxSize: number; private readonly ttlMs: number;

constructor(maxSize: number = 100, ttlMs: number = 60000) { this.cache = new Map(); this.maxSize = maxSize; this.ttlMs = ttlMs; }

get(key: string): T | undefined { const entry = this.cache.get(key); if (!entry) return undefined;

if (Date.now() > entry.expiresAt) { this.cache.delete(key); return undefined; }

// Verschiebe den Schlüssel ans Ende (neuester) this.cache.delete(key); this.cache.set(key, entry); return entry.value; }

set(key: string, value: T): void { if (this.cache.has(key)) { this.cache.delete(key); } else if (this.cache.size >= this.maxSize) { // Lösche das älteste (erste) Element const oldestKey = this.cache.keys().next().value; this.cache.delete(oldestKey); }

this.cache.set(key, { value, expiresAt: Date.now() + this.ttlMs, }); }

clear(): void { this.cache.clear(); }}

// Verwendungconst userCache = new LRUCache<User>(500, 300000); // 500 Elemente, 5 Minuten TTL


Fortgeschrittenes Pattern: Caching-Dekorator



Für sauberen Code verwenden wir TypeScript-Dekoratoren:



function Cached(ttlMs: number = 60000) {  const cache = new LRUCache<any>(100, ttlMs);

return function ( target: any, propertyKey: string, descriptor: PropertyDescriptor ) { const originalMethod = descriptor.value;

descriptor.value = async function (...args: any[]) { const key = `${propertyKey}_${JSON.stringify(args)}`; const cached = cache.get(key); if (cached !== undefined) return cached;

const result = await originalMethod.apply(this, args); cache.set(key, result); return result; };

return descriptor; };}

class UserService { @Cached(120000) // 2 Minuten async getUser(id: string): Promise<User> { // Teure Datenbankabfrage return db.findUser(id); }}


2. Redis: Verteiltes Caching mit fortgeschrittenen Datenstrukturen



Redis ist nicht nur ein Key-Value-Speicher. Seine Unterstützung für komplexe Datenstrukturen (Hash, List, Set, Sorted Set) eröffnet Möglichkeiten zur Implementierung fortgeschrittener Caching-Strategien.



Caching mit Tag-basierter Invalidierung



Problem: Beim Aktualisieren einer Entität müssen mehrere Caches ungültig gemacht werden. Lösung – Tags:



import Redis from 'ioredis';

class TaggedCache { private redis: Redis; private readonly keyPrefix: string;

constructor(redisUrl: string, prefix: string = 'cache') { this.redis = new Redis(redisUrl); this.keyPrefix = prefix; }

async get<T>(key: string,

Blogs

Buchempfehlungen