Come funziona il caching basato su chiavi?

10

Recentemente ho letto un articolo sul blog 37Signals e io rimango a chiedermi come ottengono la chiave cache.

Va tutto bene, avendo una chiave di cache che include il timestamp dell'oggetto (questo significa che quando si aggiorna l'oggetto la cache sarà invalidata); ma come si usa la chiave di cache in un modello senza causare un hit di DB per lo stesso oggetto che si sta tentando di recuperare dalla cache.

In particolare, in che modo ciò influisce sulle relazioni da uno a molti in cui stai rendendo i commenti di un post, ad esempio.

Esempio in Django:

{% for comment in post.comments.all %}
   {% cache comment.pk comment.modified %}
     <p>{{ post.body }}</p>
   {% endcache %}
{% endfor %}

Il caching in Rails è diverso dalle sole richieste di memcached ad esempio (so che convertono la tua chiave di cache in qualcosa di diverso). Memorizzano anche la cache key?

    
posta Dominic Santos 02.03.2012 - 11:43
fonte

2 risposte

3

Per il caching di una copia dritta di un singolo oggetto già caricato, sì, non ottieni nulla o next-to-nothing. Questo non è ciò che descrivono questi esempi: stanno descrivendo una gerarchia, in cui qualsiasi modifica a qualcosa di inferiore dovrebbe anche innescare un aggiornamento a tutto ciò che è più in alto nella gerarchia.

Il primo esempio, dal blog 37signals, utilizza Project -> Todolist -> Todo come gerarchia. Un esempio popolato potrebbe essere simile a questo:

Project: Foo (last_modified: 2014-05-10)
   Todolist:  Bar1 (last_modified: 2014-05-10)
       Todo:  Bang1 (last_modified: 2014-05-09)
       Todo:  Bang2 (last_modified: 2014-05-09)

   Todolist:  Bar2 (last_modified: 2014-04-01)
       Todo:  Bang3 (last_modified: 2014-04-01)
       Todo:  Bang4 (last_modified: 2014-04-01)

Quindi diciamo che Bang3 è stato aggiornato. Anche tutti i suoi genitori vengono aggiornati:

Project: Foo (last_modified: 2014-05-16)
   Todolist:  Bar2 (last_modified: 2014-05-16)
       Todo:  Bang3 (last_modified: 2014-05-16)

Quindi quando arriva il momento di eseguire il rendering, il caricamento di Project dal database è fondamentalmente inevitabile. Hai bisogno di un punto di partenza. Tuttavia, poiché il suo last_modified è un indicatore di tutti i suoi figli , è quello che usi come chiave di cache prima di caricare i bambini.

Mentre i post del blog utilizzano modelli separati, ho intenzione di raggrupparli in uno solo. Spero che vedere l'interazione completa in un unico posto lo renderà un po 'più chiaro.

Quindi, il modello Django potrebbe essere simile a questo:

{% cache 9999 project project.cache_key %}
<h2>{{ project.name }}<h2>
<div>
   {% for list in project.todolist.all %}
   {% cache 9999 todolist list.cache_key %}
      <ul>
         {% for todo in list.todos.all %}
            <li>{{ todo.body }}</li>
         {% endfor %}
      </ul>
   {% endcache %}
   {% endfor %}
</div>
{% endcache %}

Diciamo che passiamo in un Progetto il cui cache_key esiste ancora nella cache. Poiché propagiamo le modifiche a tutti gli oggetti correlati al genitore, il fatto che questa particolare chiave esista ancora significa che interi contenuti renderizzati possono essere estratti dalla cache.

Se quel particolare progetto era stato appena aggiornato - per esempio, come con Foo sopra - allora dovrà renderizzare i suoi figli, e solo allora eseguirà la query per tutti i Todol per quel progetto. Allo stesso modo per un Todolist specifico - se la cache_key di quell'elenco esiste, allora i file al suo interno non sono cambiati, e l'intera cosa può essere estratta dalla cache.

Notate anche come non sto usando todo.cache_key in questo modello. Non ne vale la pena, dal momento che come dici nella domanda, body è già stato estratto dal database. Tuttavia, gli hit del database non sono l'unica ragione per cui potresti memorizzare qualcosa. Ad esempio, prendere il testo di markup non elaborato (come quello che scriviamo in caselle di domanda / risposta su StackExchange) e convertirlo in HTML potrebbe richiedere un tempo sufficiente affinché la memorizzazione nella cache del risultato sia più efficiente.

Se così fosse, il ciclo interno nel modello potrebbe essere più simile a questo:

         {% for todo in list.todos.all %}
            {% cache 9999 todo todo.cache_key %}
               <li>{{ todo.body|expensive_markup_parser }}</li>
            {% endcache %}
         {% endfor %}

Quindi, per rimettere tutto insieme, torniamo ai miei dati originali nella parte superiore di questa risposta. Se ipotizziamo:

  • Tutti gli oggetti sono stati memorizzati nella cache nel loro stato originale
  • Bang3 è stato appena aggiornato
  • Stiamo rendendo il modello modificato (incluso expensive_markup_parser )

Quindi è così che tutto dovrebbe essere caricato:

  • Foo è recuperato dal database
  • Foo.cache_key (2014-05-16) non esiste nella cache
  • Foo.todolists.all() viene interrogato: Bar1 e Bar2 vengono recuperati dal database
  • Bar1.cache_key (2014-05-10) esiste già nella cache ; recuperalo e salvalo
  • Bar2.cache_key (2014-05-16) non esiste nella cache
  • Bar2.todos.all() viene interrogato: Bang3 e Bang4 vengono recuperati dal database
  • Bang3.cache_key (2014-05-16) non esiste nella cache
  • {{ Bang3.body|expensive_markup_parser }} è visualizzato
  • Bang4.cache_key (2014-04-01) esiste già nella cache ; recuperalo e salvalo

I risparmi dalla cache in questo piccolo esempio sono:

  • Risultato del database evitato: Bar1.todos.all()
  • expensive_markup_parser evitato 3 volte: Bang1 , Bang2 e Bang4

E, naturalmente, la prossima volta che viene visualizzato, verrà trovato Foo.cache_key , quindi l'unico costo per il rendering è recuperare Foo solo dal database e interrogare la cache.

    
risposta data 17.05.2014 - 03:36
fonte
-2

Il tuo esempio è buono se ha bisogno di recuperare o elaborare dati per ogni commento. Se prendi il corpo e lo visualizzi, la cache sarà inutile. Ma puoi memorizzare nella cache tutta la struttura dei commenti (incluso {% per%}). In questo caso è necessario invalidarlo con ogni commento aggiunto, in modo che tu possa inserire il conteggio dell'ultimo commento o il conteggio dei commenti da qualche parte in Post e creare la chiave cache dei commenti con esso. Se si preferiscono dati più normalizzati e si utilizzano commenti su una sola pagina, è sufficiente cancellare una chiave di cache per salvare i commenti.

Per me, il conteggio dei commenti conta in Post sembra abbastanza buono (se non permetti di eliminare e modificare i commenti) - hai un valore da mostrare ovunque con il Post e una chiave per la memorizzazione nella cache.

    
risposta data 03.03.2012 - 02:33
fonte

Leggi altre domande sui tag