Heim > Web-Frontend > js-Tutorial > So erstellen Sie einen In-Memory-Cache

So erstellen Sie einen In-Memory-Cache

Patricia Arquette
Freigeben: 2024-12-27 02:37:09
Original
670 Leute haben es durchsucht

How to Create an In-Memory Cache

Bei vielen Projekten ist mir aufgefallen, dass ein Cache zwar praktisch sein kann – insbesondere auf der Clientseite –, aber oft übersehen wird. Clientseitiges Caching ist entscheidend für die Verbesserung des Benutzererlebnisses, indem es die Latenz reduziert und wiederholte Serveranfragen entlastet. Beispielsweise verhindert das Zwischenspeichern zuvor abgerufener Daten in Anwendungen mit unendlichem Scrollen oder häufig aktualisierten Dashboards unnötige API-Aufrufe und sorgt so für reibungslosere Interaktionen und schnellere Renderzeiten.

In einem meiner jüngsten Projekte reduzierte die Implementierung eines Caches das API-Aufrufvolumen um über 40 %, was zu spürbaren Leistungsverbesserungen und Kosteneinsparungen führte. Dies unterstreicht, warum clientseitiges Caching als grundlegende Optimierungsstrategie betrachtet werden sollte. Ein Cache ist tendenziell eine der letzten berücksichtigten Funktionen, obwohl er bei relativ einfacher Implementierung erhebliche Auswirkungen auf die Leistung hat, sei es aufgrund von Einschränkungen bei der Entwicklungszeit oder anderen Prioritäten.

Ein Cache kann auf verschiedenen Ebenen der Architektur implementiert werden: vom Backend-Caching mit Redis, einem CDN für statische Inhalte, bis hin zu einem In-Memory-Cache auf dem Client oder sogar der Verwendung von localStorage oder IndexedDB für Persistenz. Idealerweise sollten diese Strategien kombiniert werden, um die Belastung und Kosten von Datenbanken und APIs sowie die Verzögerung durch Client-Server-Anfragen zu reduzieren, insbesondere für Daten, die bereits zuvor abgerufen wurden.

In diesem Artikel erfahren Sie, wie Sie einen LRU-Cache (Least Recent Used) mit TTL-Unterstützung (Time-to-Live) in JavaScript entwerfen und implementieren und so ein Paket erstellen, das meinem adev-lru ähnelt. Am Ende verfügen Sie über ein funktionierendes Beispiel, das die Kernprinzipien und Funktionalität einer effektiven Caching-Lösung demonstriert.


Was ist ein LRU-Cache?

Ein LRU-Cache (Least Recent Used) stellt sicher, dass die Elemente, auf die zuletzt zugegriffen wurde, im Speicher verbleiben, während die Elemente, auf die zuletzt zugegriffen wurde, gelöscht werden, wenn ihre Kapazität überschritten wird. Bei dieser Strategie wird eine Nutzungsreihenfolge eingehalten: Jedes Zubehör aktualisiert die Position des Elements im Cache, wobei die Elemente, auf die am wenigsten zugegriffen wird, zuerst entfernt werden.

Im Vergleich zu anderen Caching-Strategien bietet LRU ein ausgewogenes Verhältnis zwischen Einfachheit und Effizienz und eignet sich daher gut für Szenarien, in denen die aktuelle Nutzung ein zuverlässiger Indikator für den zukünftigen Zugriff ist. Beispielsweise können Anwendungen, die API-Antworten, Miniaturansichten oder häufig aufgerufene Benutzereinstellungen zwischenspeichern, LRU nutzen, um redundante Abrufvorgänge zu reduzieren, ohne den Löschvorgang übermäßig zu komplizieren.

Im Gegensatz zu LFU (Least Frequently Used), das die Zugriffshäufigkeit verfolgt und zusätzliche Buchhaltung erfordert, vermeidet LRU diese Komplexität und erzielt dennoch in vielen realen Anwendungsfällen eine hervorragende Leistung. In ähnlicher Weise bieten FIFO (First In, First Out) und MRU (Most Recent Used) alternative Räumungsrichtlinien, passen jedoch möglicherweise nicht so gut zu Nutzungsmustern, bei denen die jüngste Aktivität von entscheidender Bedeutung ist. Durch die Kombination von LRU mit TTL-Unterstützung (Time-to-Live) in meiner Implementierung werden auch Szenarien verarbeitet, in denen Daten automatisch ablaufen müssen, wodurch die Anwendbarkeit in dynamischen Umgebungen wie Live-Dashboards oder Streaming-Diensten weiter verbessert wird. Dies ist besonders nützlich bei Anwendungen, bei denen der Zugriff auf die neuesten Daten von entscheidender Bedeutung ist.

Durchführung

Die LRUCache-Klasse ist darauf ausgelegt, effizient zu sein, flexible Konfigurationen zu unterstützen und automatische Räumungen durchzuführen. Nachfolgend sind einige wichtige Methoden aufgeführt:

Den Cache erstellen

public static getInstance<T>(capacity: number = 10): LRUCache<T> {
    if (LRUCache.instance == null) {
        LRUCache.instance = new LRUCache<T>(capacity);
    }
    return LRUCache.instance;
}
Nach dem Login kopieren
Nach dem Login kopieren

Diese Methode stellt sicher, dass es nur eine Instanz des Caches in der Anwendung gibt, eine Designwahl, die die Ressourcenverwaltung vereinfacht. Durch die Implementierung des Caches als Singleton vermeiden wir redundante Speichernutzung und stellen konsistente Daten in der gesamten Anwendung sicher. Dies ist besonders wertvoll in Szenarien, in denen mehrere Komponenten oder Module Zugriff auf dieselben zwischengespeicherten Daten benötigen, da es Konflikte verhindert und die Synchronisierung gewährleistet, ohne dass zusätzliche Koordinationslogik erforderlich ist. Wenn keine Kapazität angegeben ist, wird standardmäßig 10 verwendet.

Hinzufügen eines Elements zum Cache

public put(key: string, value: T, ttl: number = 60_000): LRUCache<T> {
    const now = Date.now();
    let node = this.hash.get(key);
    if (node != null) {
        this.evict(node);
    }
    node = this.prepend(key, value, now + ttl);
    this.hash.set(key, node);
    if (this.hash.size > this.capacity) {
        const tailNode = this.pop();
        if (tailNode != null) {
            this.hash.delete(tailNode.key);
        }
    }
    return this;
}
Nach dem Login kopieren
Nach dem Login kopieren

Diese Methode fügt ein Element im Cache hinzu oder aktualisiert es. Wenn ein Schlüssel bereits vorhanden ist, wird das entsprechende Element entfernt und vorne im Cache wieder hinzugefügt. Zu diesem Zweck verwendet der Cache eine doppelt verknüpfte Liste, um die Daten als Knoten zu speichern und die Möglichkeit beizubehalten, Daten vom Ende der Liste (Ende) zu löschen und an den Anfang der Liste (Kopf) zu verschieben, um ein konstantes O zu gewährleisten (1) Beim Lesen der Daten jedes Knotens wird eine Hash-Tabelle verwendet, um einen Zeiger auf jeden Knoten der Liste zu speichern. Dieser Prozess steht im Einklang mit dem LRU-Prinzip, indem sichergestellt wird, dass kürzlich aufgerufene Elemente immer priorisiert werden und sie effektiv als „zuletzt verwendet“ gekennzeichnet werden. Auf diese Weise behält der Cache eine genaue Nutzungsreihenfolge bei, die für die Räumungsentscheidung bei Überschreitung der Kapazität von entscheidender Bedeutung ist. Dieses Verhalten gewährleistet eine optimale Ressourcenverwaltung und minimiert die Abrufzeit für häufig aufgerufene Daten. Wenn der Schlüssel bereits vorhanden ist, wird das Element nach vorne verschoben, um es als zuletzt verwendet zu markieren.

Abrufen eines Elements aus dem Cache

public get(key: string): T | undefined {
    const node = this.hash.get(key);
    const now = Date.now();
    if (node == null || node.ttl < now) {
        return undefined;
    }
    this.evict(node);
    this.prepend(node.key, node.value, node.ttl);
    return node.value;
}
Nach dem Login kopieren

Diese Methode ruft gespeicherte Elemente ab. Wenn der Artikel abgelaufen ist, wird er aus dem Cache entfernt.

Leistungskennzahlen

Um die Effizienz des Caches zu bewerten, habe ich Leistungsmetriken wie Trefferquote, Fehlschläge und Räumungen implementiert:

public static getInstance<T>(capacity: number = 10): LRUCache<T> {
    if (LRUCache.instance == null) {
        LRUCache.instance = new LRUCache<T>(capacity);
    }
    return LRUCache.instance;
}
Nach dem Login kopieren
Nach dem Login kopieren

Cache leeren

public put(key: string, value: T, ttl: number = 60_000): LRUCache<T> {
    const now = Date.now();
    let node = this.hash.get(key);
    if (node != null) {
        this.evict(node);
    }
    node = this.prepend(key, value, now + ttl);
    this.hash.set(key, node);
    if (this.hash.size > this.capacity) {
        const tailNode = this.pop();
        if (tailNode != null) {
            this.hash.delete(tailNode.key);
        }
    }
    return this;
}
Nach dem Login kopieren
Nach dem Login kopieren

Diese Methode löscht alle Elemente und setzt den Cache-Status zurück.

In meiner Implementierung habe ich auch andere Methoden wie getOption hinzugefügt, die statt T | zurückzugeben undefiniert Es gibt eine Instanz der Monad-Option zurück für diejenigen, die einen funktionaleren Ansatz bevorzugen. Ich habe außerdem eine Writer-Monade hinzugefügt, um jeden Vorgang im Cache zu Protokollierungszwecken zu verfolgen.

Sie können alle anderen an diesem Algorithmus beteiligten Methoden, sehr gut kommentiert, in diesem Repository sehen: https://github.com/Armando284/adev-lru


Vergleich von Cache-Algorithmen

Ein LRU-Cache ist nicht die einzige Option. Die Wahl des richtigen Caching-Algorithmus hängt stark von den spezifischen Anforderungen und Zugriffsmustern der Anwendung ab. Nachfolgend finden Sie einen Vergleich von LRU mit anderen häufig verwendeten Caching-Strategien und Hinweise, wann diese jeweils zu verwenden sind:

  • LFU (Least Frequently Used): Dieser Algorithmus entfernt Elemente, auf die am seltensten zugegriffen wird. Dies ist ideal für Szenarien, in denen die Nutzungshäufigkeit im Zeitverlauf einen besseren Indikator für zukünftige Zugriffe darstellt als die Aktualität. Allerdings erfordert es in der Regel eine zusätzliche Buchhaltung, um die Zugriffszahlen zu verfolgen, was es komplexer und rechenintensiver als LRU macht. Verwenden Sie LFU für Anwendungen wie Empfehlungs-Engines oder Pipelines für maschinelles Lernen, bei denen historische Nutzungsmuster von entscheidender Bedeutung sind.
  • FIFO (First In, First Out): Bei diesem Ansatz werden die ältesten Elemente entfernt, unabhängig davon, wie oft auf sie zugegriffen wird. Obwohl es einfach zu implementieren ist, ist FIFO für die meisten Anwendungsfälle möglicherweise nicht geeignet, da es keine Nutzungsmuster berücksichtigt. Es kann für Anwendungen mit festen, vorhersehbaren Arbeitsabläufen funktionieren, wie zum Beispiel das Zwischenspeichern statischer Konfigurationen oder vorinstallierter Assets.
  • MRU (Zuletzt verwendet): MRU entfernt die zuletzt aufgerufenen Elemente, das Gegenteil von LRU. Diese Strategie eignet sich am besten für Situationen, in denen ältere Daten mit größerer Wahrscheinlichkeit wiederverwendet werden, wie z. B. Rollback-Systeme oder bestimmte Arten von Rückgängig-Vorgängen.

Wann sollte LRU verwendet werden?

LRU schafft ein Gleichgewicht zwischen Einfachheit und Effektivität und eignet sich daher ideal für Anwendungen, bei denen die jüngste Aktivität stark mit der zukünftigen Nutzung korreliert. Zum Beispiel:

  • Web- und API-Caching: LRU eignet sich gut zur Reduzierung redundanter API-Aufrufe, insbesondere in Szenarien wie paginierten Ansichten, unendlichem Scrollen oder häufigem Abfragen von Live-Daten.
  • Multimedia-Anwendungen: Zwischenspeichern kürzlich abgespielter oder angesehener Elemente wie Videos oder Bilder.
  • UI-Statusverwaltung: Speichern Sie kürzlich aufgerufene Komponentenzustände, um die Rendering-Leistung zu verbessern.

Wenn dagegen Zugriffsmuster zeigen, dass Häufigkeit oder Einfügungsreihenfolge relevanter sind, sind Algorithmen wie LFU oder FIFO möglicherweise die bessere Wahl. Durch die Bewertung dieser Kompromisse wird sichergestellt, dass die Caching-Strategie mit den Zielen und Ressourcenbeschränkungen Ihrer Anwendung übereinstimmt.

  • LFU (Least Frequently Used): Entfernt die am wenigsten aufgerufenen Elemente basierend auf der Häufigkeit.
  • FIFO (First In, First Out): Entfernt das älteste Element, unabhängig von der letzten Verwendung.
  • MRU (Zuletzt verwendet): Entfernt im Gegensatz zu LRU die zuletzt hinzugefügten Elemente.

Abschluss

Die Implementierung eines In-Memory-Cache kann die Leistung einer Anwendung erheblich steigern, Reaktionszeiten verkürzen und die Benutzererfahrung verbessern.

Wenn Sie einen vollständigen LRU-Cache in Aktion sehen möchten, können Sie mein npm-Paket https://www.npmjs.com/package/adev-lru verwenden. Ich würde mich auch über Ihr Feedback freuen, um es weiter zu verbessern.

Probieren Sie das Paket aus und teilen Sie Ihre Gedanken mit oder tragen Sie bei, wenn Sie Lust haben, mehr zu helfen?!

Das obige ist der detaillierte Inhalt vonSo erstellen Sie einen In-Memory-Cache. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Quelle:dev.to
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Neueste Artikel des Autors
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage