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 --> SearchLeistungsvergleich
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
-
Memory-mapped file - Wikipedia
-
Key-value database - Wikipedia
-
Vector database - Wikipedia
Verwandte Artikel
-
SEO Pipeline Overview - Komplette Pipeline-Architektur
-
Search Service Architecture - Live-Suche mit Valkey
-
Multi-Server Architecture - Speicher pro Server
-
Incremental Processing - Caching-Strategie
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.