Heim >Backend-Entwicklung >Python-Tutorial >Python-Caching veränderlicher Werte
Caching beschleunigt die Verarbeitung erheblich, von Vorgängen auf CPU-Ebene bis hin zu Datenbankschnittstellen. Die Cache-Invalidierung – also die Bestimmung, wann zwischengespeicherte Daten entfernt werden müssen – ist eine komplexe Herausforderung. Dieser Beitrag befasst sich mit einem einfacheren, aber heimtückischen Caching-Problem.
Dieses Problem, das 18 Monate lang lauerte, trat erst auf, als Benutzer vom empfohlenen Nutzungsmuster abwichen. Das Problem entstand durch ein benutzerdefiniertes Framework für maschinelles Lernen (ML) (basierend auf scikit-learn) in meiner Organisation. Dieses Framework greift häufig auf mehrere Datenquellen zu und erfordert eine Caching-Ebene zur Leistungs- und Kostenoptimierung (Reduzierung der BigQuery-Ausgangskosten).
Anfänglich wurde lru_cache
verwendet, aber für statische Daten, auf die während der Entwicklung häufig zugegriffen wird, war ein persistenter Cache erforderlich. DiskCache
, eine Python-Bibliothek mit SQLite, wurde aufgrund ihrer Einfachheit und Kompatibilität mit unserer 32-Prozess-Umgebung und Pandas DataFrames (bis zu 500 MB) ausgewählt. Darüber wurde eine lru_cache
-Ebene für den In-Memory-Zugriff hinzugefügt.
Das Problem trat auf, als mehr Benutzer mit dem Framework experimentierten. Es wurden zufällig falsche Ergebnisse gemeldet, die sich nur schwer konsistent reproduzieren ließen. Die Hauptursache: direkte Änderung der zwischengespeicherten Pandas DataFrames.
Unser Codierungsstandard erforderte die Erstellung neuer DataFrames nach jeder Verarbeitung. Einige Benutzer verwendeten jedoch aus Gewohnheit inplace=True
und änderten das zwischengespeicherte Objekt direkt. Dadurch wurden nicht nur die unmittelbaren Ergebnisse verändert, sondern auch die zwischengespeicherten Daten beschädigt, was sich auf nachfolgende Anfragen auswirkte.
Betrachten Sie zur Veranschaulichung dieses vereinfachte Beispiel mit Wörterbüchern:
<code class="language-python">from functools import lru_cache import time import typing as t from copy import deepcopy @lru_cache def expensive_func(keys: str, vals: t.Any) -> dict: time.sleep(3) return dict(zip(keys, vals)) def main(): e1 = expensive_func(('a', 'b', 'c'), (1, 2, 3)) print(e1) e2 = expensive_func(('a', 'b', 'c'), (1, 2, 3)) print(e2) e2['d'] = "amazing" print(e2) e3 = expensive_func(('a', 'b', 'c'), (1, 2, 3)) print(e3) if __name__ == "__main__": main()</code>
lru_cache
stellt eine Referenz dar, keine Kopie. Durch Ändern von e2
werden die zwischengespeicherten Daten geändert.
Lösung:
Die Lösung besteht darin, eine tiefe Kopie des zwischengespeicherten Objekts zurückzugeben:
<code class="language-python">from functools import lru_cache, wraps from copy import deepcopy def custom_cache(func): cached_func = lru_cache(func) @wraps(func) def _wrapper(*args, **kwargs): return deepcopy(cached_func(*args, **kwargs)) return _wrapper</code>
Dies führt zu einem geringen Mehraufwand (Datenduplizierung), verhindert jedoch eine Datenbeschädigung.
Wichtige Erkenntnisse:
lru_cache
.Das obige ist der detaillierte Inhalt vonPython-Caching veränderlicher Werte. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!