Storage-Strategie: Valkey vs JSON vs NumPy

Dieser Artikel erklärt, warum wir in der SEO-Pipeline und im Suchdienst unterschiedliche Speichertechnologien für verschiedene Datentypen verwenden.

Das Problem: Eine Lösung passt nicht für alle

Verschiedene Daten haben unterschiedliche Zugriffsmuster:

  • Embeddings (5K x 384 Floats): Benötigen schnelle Vektoroperationen (Kosinusähnlichkeit)

  • Phrasenzuordnungen (2500+ Phrasen): Benötigen menschliche Bearbeitung, Versionskontrolle

  • Abfrage-Cache (Live-Abfragen): Benötigen schnelle Key-Value-Lookups, TTL-Ablauf

  • Produktdaten (64K Produkte): Benötigen strukturierten Zugriff, Kompatibilitätsregeln

Die Verwendung desselben Speichers für alles wäre ineffizient.

Drei Speichertechnologien

1. NumPy-Arrays: Vektoroperationen

Anwendungsfall: Embeddings (Produkte, Abfragen, Phrasen)

Warum NumPy:

  • Schnelle Vektormathematik: Optimierte C/Fortran-Bibliotheken für Matrixoperationen

  • Speichergemappte Dateien: Laden großer Arrays ohne Kopieren in den RAM

  • Batch-Operationen: Verarbeiten Tausender Vektoren in Millisekunden

  • Standardformat: Kompatibel mit ML-Bibliotheken (scikit-learn, TensorFlow)

Dateiformat: Binäre .npy-Dateien

Laden:

import numpy as np

# Speichergemappt (lädt nicht die gesamte Datei in den RAM)
embeddings = np.load('embeddings.npy', mmap_mode='r')

# Kosinusähnlichkeit berechnen
from sklearn.metrics.pairwise import cosine_similarity
similarities = cosine_similarity(query_embedding, embeddings)

Leistung: Massive Ähnlichkeitssuchen mit Geschwindigkeiten, die für Echtzeit-Inferenz geeignet sind.

2. JSON-Dateien: Vom Menschen bearbeitbare Daten

Anwendungsfall: Konfiguration, Zuordnungen, Metadaten

Warum JSON:

  • Menschenlesbar: Einfach zu inspizieren und zu debuggen

  • Versionskontrolle: Git-Diffs zeigen genau, was sich geändert hat

  • Manuelle Bearbeitung: Fehler können ohne Code behoben werden

  • Universelles Format: Jede Sprache kann JSON parsen

Was wir speichern:

  • Phrase-zu-Filter-Zuordnungen: Phrasen → Filterregeln

  • Produktfeatures: Produkte → Feature-Wörterbücher

  • Abfrage-Metadaten: Abfragen → Klicks, Impressionen, Quellen

  • Pipeline-Konfiguration: Schrittparameter, Schwellenwerte

Dateiformat: Text-.json-Dateien

Laden:

import json

with open('phrase_mappings.json') as f:
    mappings = json.load(f)

# Auf Daten zugreifen
filters = mappings['mini pc']  # ['form_factor:mini', 'category:pc']

3. Valkey (Redis): Schneller Key-Value-Cache

Anwendungsfall: Live-Suche, Autovervollständigung, beliebte Abfragen

Warum Valkey:

  • Im Speicher: Mikrosekunden-Latenz für Lookups

  • RediSearch: Vektor-Ähnlichkeitssuche mit Indizes

  • TTL-Ablauf: Automatische Cache-Invalidierung

  • Pub/sub: Echtzeit-Updates über Server hinweg

  • Persistenz: Optionale Disk-Snapshots für Dauerhaftigkeit

Was wir speichern:

  • Abfrage-Embeddings-Cache: Aktuelle Abfragen → Embeddings

  • Beliebte Abfragen: Top 1000 Abfragen nach Traffic

  • Autovervollständigungs-Index: Präfix → Abfragevorschläge

  • Filter-Extraktions-Cache: Abfrage → extrahierte Filter

  • Verwandte-Suchen-Cache: Abfrage → verwandte Abfragen

Datenstrukturen:

  • Strings: Einfache Key-Value-Paare (Abfrage → Embedding)

  • Sortierte Sets: Rangierte Daten (beliebte Abfragen nach Score)

  • RediSearch-Indizes: Vektor-Ähnlichkeitssuche

  • Hashes: Strukturierte Daten (Abfrage-Metadaten)

Laden:

from app.shared.valkey_cache import get_valkey

valkey = get_valkey()
# ... (Implementierungsdetails ausgelassen)

Entscheidungsmatrix

Wann NumPy verwenden

Kriterien:

  • Daten sind numerisch (Floats, Ints)

  • Schnelle Vektoroperationen benötigt (Skalarprodukt, Kosinusähnlichkeit)

  • Daten sind leselastig (selten aktualisiert)

  • Daten sind groß (Millionen von Zahlen)

  • Batch-Verarbeitung (viele Elemente auf einmal verarbeiten)

Beispiele:

  • Embeddings (Produkte, Abfragen, Phrasen)

  • Feature-Vektoren

  • Ähnlichkeitsmatrizen

Wann JSON verwenden

Kriterien:

  • Daten sind strukturiert (Objekte, Arrays)

  • Menschenlesbarkeit benötigt

  • Versionskontrolle (Git) benötigt

  • Daten ändern sich gelegentlich (manuelle Bearbeitung)

  • Daten sind klein bis mittel (<100 MB)

Beispiele:

  • Konfigurationsdateien

  • Phrasenzuordnungen

  • Produktmetadaten

  • Pipeline-Parameter

Wann Valkey verwenden

Kriterien:

  • Schnelle Lookups benötigt (Mikrosekunden)

  • Daten ändern sich häufig (Live-Abfragen)

  • TTL-Ablauf benötigt (Cache-Invalidierung)

  • Pub/sub benötigt (Echtzeit-Updates)

  • Vektorsuche benötigt (RediSearch)

Beispiele:

  • Abfrage-Cache

  • Autovervollständigung

  • Beliebte Abfragen

  • Sitzungsdaten

  • Ratenbegrenzung

Hybrider Ansatz

Wir kombinieren alle drei für optimale Leistung:

Pipeline (Offline-Verarbeitung)

NumPy: Embeddings, Ähnlichkeitsmatrizen berechnen

JSON: Zuordnungen, Metadaten, Konfiguration speichern

Valkey: Wird nicht verwendet (Pipeline läuft offline)

Suchdienst (Live-Abfragen)

NumPy: Embeddings von der Festplatte laden (speichergemappt)

JSON: Zuordnungen von der Festplatte laden (im Speicher gecached)

Valkey: Live-Abfragen, Autovervollständigung, beliebte Abfragen cachen

Datenfluss

graph LR
    Pipeline[SEO Pipeline
Offline] subgraph Storage NP[NumPy Files
embeddings.npy] JS[JSON Files
mappings.json] end Search[Search Service
Live] VK[Valkey
Cache] Pipeline --> NP Pipeline --> JS NP --> Search JS --> Search Search --> VK VK --> Search

Leistungsvergleich

NumPy (speichergemappt):

  • Ladezeit: Sofort (Zero-Copy-Mapping auf virtuellen Speicher)

  • Lookup-Zeit: Nahezu Null (direkter CPU-Zeiger auf Array-Index)

  • Speicher: Minimal (vom Betriebssystem verwalteter Page-Cache, nicht im Prozess-RAM resident)

JSON:

  • Ladezeit: Hohe Latenz (sequenzielles Parsen und Objektinstanziierung)

  • Lookup-Zeit: Effizient (Standard-Hash-Map-Overhead)

  • Speicher: Erheblich (gesamte serialisierte Struktur befindet sich auf dem Heap)

Valkey:

  • Ladezeit: Persistent (im Hintergrunddienst resident)

  • Lookup-Zeit: Mäßig (beinhaltet Netzwerk-Roundtrip und Protokoll-Serialisierung)

  • Speicher: Externalisiert (innerhalb des Datenbankprozesses isoliert)

Gewinner: NumPy für hohen Durchsatz im Batch-Betrieb, Valkey für verteilten Einzelschlüssel-Zugriff

Vektor-Ähnlichkeitssuche

NumPy (cosine_similarity):

  • Batch-Operationen: Schnell (optimiert via SIMD/vektorisierter linearer Algebra)

  • Parallelisierbar: Hoch (skaliert über alle verfügbaren physischen CPU-Kerne)

  • Speicher: Linear (skaliert direkt mit den Embedding-Dimensionen)

Valkey (RediSearch):

  • Suchgeschwindigkeit: Überlegen (nutzt spezialisierte HNSW/Vektor-Indizierung)

  • Parallelisierbar: Begrenzt (durch das Threading-Modell der Engine eingeschränkt)

  • Speicher: Intensiv (benötigt rohe Embeddings plus Indexierungs-Metadaten)

Gewinner: Valkey für sub-perzeptive Live-Suche, NumPy für schwere Offline-Analyseverarbeitung

Konfigurations-Lookup

JSON:

  • Ladezeit: Variabel (proportional zur Konfigurationskomplexität)

  • Lookup-Zeit: Schnell (nativ Dictionary-Zugriff)

  • Bearbeitungszeit: Reibungslos (menschenlesbare Textmodifikation)

Valkey:

  • Ladezeit: Sofort (aktiv bei Verbindung)

  • Lookup-Zeit: Netzwerkgebunden (abhängig vom Request-Overhead)

  • Bearbeitungszeit: Programmatisch (erfordert CLI oder Client-SET-Befehle)

Gewinner: JSON für statische Konfigurationen, Valkey für dynamischen gemeinsamen Zustand

Referenzen

Technologien

  • NumPy - Array-Computing-Bibliothek

  • JSON - Datenaustauschformat

  • Valkey - In-Memory-Datenspeicher (Redis-Fork)

  • RediSearch - Vektor-Suchmodul

Technische Konzepte

Verwandte Artikel

Zusammenfassung

Wir verwenden drei Speichertechnologien, die für verschiedene Anwendungsfälle optimiert sind:

NumPy (Embeddings):

  • Schnelle Vektoroperationen (Kosinusähnlichkeit)

  • Speichergemappt

  • Batch-Verarbeitung

  • Standard-ML-Format

JSON (Konfiguration):

  • Menschenlesbar (einfaches Debugging)

  • Versionskontrolle (Git-Diffs)

  • Manuelle Bearbeitung (kein Code nötig)

  • Universelles Format

Valkey (Live-Cache):

  • Schnelle Lookups (Mikrosekunden)

  • Vektorsuche (RediSearch)

  • TTL-Ablauf (Auto-Invalidierung)

  • Pub/sub (Echtzeit-Updates)

Hybrider Ansatz: Verwende das richtige Werkzeug für jede Aufgabe, kombiniere für optimale Leistung.


← Zurück zum Dokumentationsindex