Ich habe kürzlich einen Artikel im 37Signals-Blog gelesen und frage mich, wie sie den Cache-Schlüssel erhalten.
Es ist schön und gut, einen Cache-Schlüssel zu haben, der den Zeitstempel des Objekts enthält (dies bedeutet, dass der Cache ungültig wird, wenn Sie das Objekt aktualisieren). Aber wie verwenden Sie dann den Cache-Schlüssel in einer Vorlage, ohne einen DB-Treffer für genau das Objekt zu verursachen, das Sie aus dem Cache abrufen möchten?
Wie wirkt sich dies insbesondere auf die Eins-zu-Viele-Beziehungen aus, in denen Sie beispielsweise die Kommentare eines Posts rendern?
Beispiel in Django:
{% for comment in post.comments.all %}
{% cache comment.pk comment.modified %}
<p>{{ post.body }}</p>
{% endcache %}
{% endfor %}
Unterscheidet sich das Caching in Rails beispielsweise nur von Anfragen an Memcached (ich weiß, dass sie Ihren Cache-Schlüssel in etwas anderes konvertieren). Zwischenspeichern sie auch den Cache-Schlüssel?
post.body
sollte es seincomment.body
?Antworten:
Ja, wenn Sie einen geraden Speicherauszug eines einzelnen bereits geladenen Objekts zwischenspeichern, erhalten Sie nichts oder so gut wie nichts. Das beschreiben diese Beispiele nicht - sie beschreiben eine Hierarchie, bei der jede Änderung an etwas Niedrigerem auch eine Aktualisierung aller höheren Elemente in der Hierarchie auslösen sollte.
Das erste Beispiel aus dem 37signals-Blog wird
Project -> Todolist -> Todo
als Hierarchie verwendet. Ein ausgefülltes Beispiel könnte folgendermaßen aussehen:Nehmen wir also an, es
Bang3
wurde aktualisiert. Alle Eltern werden ebenfalls aktualisiert:Wenn es dann Zeit zum Rendern ist, ist das Laden
Project
aus der Datenbank grundsätzlich unvermeidlich. Sie brauchen einen Punkt, um anzufangen. Dalast_modified
es sich jedoch um einen Indikator für alle untergeordneten Elemente handelt, verwenden Sie diesen als Cache-Schlüssel, bevor Sie versuchen, die untergeordneten Elemente zu laden.Während die Blog-Beiträge separate Vorlagen verwenden, werde ich sie zu einer zusammenfassen. Hoffentlich wird die vollständige Interaktion an einem Ort etwas klarer.
Die Django-Vorlage könnte also ungefähr so aussehen:
Angenommen, wir übergeben ein Projekt, dessen
cache_key
Cache noch vorhanden ist. Da wir Änderungen an allen zugehörigen Objekten an das übergeordnete Objekt weitergeben, bedeutet die Tatsache, dass dieser bestimmte Schlüssel noch vorhanden ist, dass der gesamte gerenderte Inhalt aus dem Cache abgerufen werden kann.Wenn dieses bestimmte Projekt gerade aktualisiert wurde - beispielsweise wie
Foo
oben -, muss es seine untergeordneten Elemente rendern und erst dann die Abfrage für alle Todolisten für dieses Projekt ausführen. Ebenso für eine bestimmte Todolist - wenn der cache_key dieser Liste vorhanden ist, haben sich die darin enthaltenen Aufgaben nicht geändert, und das Ganze kann aus dem Cache gezogen werden.Beachten Sie auch, dass ich
todo.cache_key
diese Vorlage nicht verwende . Es lohnt sich nicht, da, wie Sie in der Frage sagen,body
bereits aus der Datenbank gezogen wurde. Datenbanktreffer sind jedoch nicht der einzige Grund, warum Sie möglicherweise etwas zwischenspeichern. Wenn Sie beispielsweise rohen Markup-Text (z. B. das, was wir in StackExchange in Frage- / Antwortfelder eingeben) und in HTML konvertieren, kann dies ausreichend Zeit in Anspruch nehmen, damit das Zwischenspeichern des Ergebnisses effizienter ist.In diesem Fall könnte die innere Schleife in der Vorlage folgendermaßen aussehen:
Um alles zusammenzuführen, kehren wir zu meinen ursprünglichen Daten oben in dieser Antwort zurück. Wenn wir annehmen:
Bang3
wurde gerade aktualisiertexpensive_markup_parser
)Dann würde so alles geladen:
Foo
wird aus der Datenbank abgerufenFoo.cache_key
(2014-05-16) existiert nicht im CacheFoo.todolists.all()
wird abgefragt:Bar1
undBar2
werden aus der Datenbank abgerufenBar1.cache_key
(2014-05-10) existiert bereits im Cache ; Abrufen und AusgebenBar2.cache_key
(2014-05-16) existiert nicht im CacheBar2.todos.all()
wird abgefragt:Bang3
undBang4
werden aus der Datenbank abgerufenBang3.cache_key
(2014-05-16) existiert nicht im Cache{{ Bang3.body|expensive_markup_parser }}
ist gerendertBang4.cache_key
(2014-04-01) existiert bereits im Cache ; Abrufen und AusgebenEinsparungen aus dem Cache in diesem winzigen Beispiel sind:
Bar1.todos.all()
expensive_markup_parser
vermieden 3 mal:Bang1
,Bang2
, undBang4
Und natürlich wird es beim nächsten Anzeigen
Foo.cache_key
gefunden, sodass die einzigen Kosten für das Rendern darin bestehen,Foo
allein aus der Datenbank abzurufen und den Cache abzufragen.quelle
Ihr Beispiel ist gut, wenn für jeden Kommentar Daten abgerufen oder verarbeitet werden müssen. Wenn Sie nur body nehmen und anzeigen, ist der Cache nutzlos. Sie können jedoch den gesamten Kommentarbaum zwischenspeichern (einschließlich {% für%}). In diesem Fall müssen Sie es mit jedem hinzugefügten Kommentar ungültig machen, damit Sie den Zeitstempel des letzten Kommentars oder die Anzahl der Kommentare irgendwo in Post einfügen und damit den Cache-Schlüssel für Kommentare erstellen können. Wenn Sie normalisierte Daten bevorzugen und Kommentare nur auf einer Seite verwenden, können Sie beim Speichern von Kommentaren einfach einen Cache-Schlüssel löschen.
Für mich sieht das Speichern der Anzahl der Kommentare in Post gut genug aus (wenn Sie das Löschen und Bearbeiten von Kommentaren nicht zulassen) - Sie haben einen Wert, den Sie überall mit dem Post anzeigen können, und einen Schlüssel zum Zwischenspeichern.
quelle