Hace poco leí un artículo en el blog 37Signals y me pregunto cómo es que obtienen la clave de caché.
Está muy bien tener una clave de caché que incluya la marca de tiempo del objeto (esto significa que cuando actualice el objeto, la caché se invalidará); pero, ¿cómo se usa la clave de caché en una plantilla sin causar un golpe de base de datos para el objeto que está tratando de recuperar de la caché?
Específicamente, ¿cómo afecta esto a las relaciones de uno a muchos, por ejemplo, cuando representa los comentarios de una publicación?
Ejemplo en Django:
{% for comment in post.comments.all %}
{% cache comment.pk comment.modified %}
<p>{{ post.body }}</p>
{% endcache %}
{% endfor %}
El almacenamiento en caché en Rails es diferente a las solicitudes de memcached, por ejemplo (sé que convierten su clave de caché en algo diferente). ¿También guardan en caché la clave de caché?
post.body
destinado a sercomment.body
?Respuestas:
Para almacenar en caché un volcado directo de un solo objeto ya cargado, sí, no gana nada o casi nada. Eso no es lo que esos ejemplos están describiendo: están describiendo una jerarquía, donde cualquier cambio a algo más bajo también debería desencadenar una actualización de todo lo que está más arriba en la jerarquía.
El primer ejemplo, del blog 37signals, se usa
Project -> Todolist -> Todo
como jerarquía. Un ejemplo poblado podría verse así:Entonces, digamos que
Bang3
se actualizó. Todos sus padres también se actualizan:Luego, cuando llega el momento de renderizar, la carga
Project
desde la base de datos es básicamente inevitable. Necesitas un punto para comenzar. Sin embargo, debido a quelast_modified
es un indicador de todos sus elementos secundarios , eso es lo que utiliza como clave de caché antes de intentar cargar los elementos secundarios.Si bien las publicaciones del blog usan plantillas separadas, las agruparé en una sola. Con suerte, ver la interacción completa en un lugar lo hará un poco más claro.
Entonces, la plantilla de Django podría verse así:
Digamos que pasamos un proyecto cuyo
cache_key
todavía existe en el caché. Debido a que propagamos los cambios a todos los objetos relacionados al padre, el hecho de que esa clave en particular aún exista significa que todo el contenido renderizado se puede extraer de la memoria caché.Si ese Proyecto en particular acaba de actualizarse, por ejemplo, como en el caso
Foo
anterior, tendrá que representar a sus hijos, y solo entonces ejecutará la consulta para todos los Todolistas para ese Proyecto. Del mismo modo para un Todolist específico: si la clave_caché de esa lista existe, entonces los todos dentro de ella no han cambiado, y todo se puede extraer de la caché.Observe también cómo no estoy usando
todo.cache_key
en esta plantilla. No vale la pena, ya que como dices en la pregunta,body
ya se ha retirado de la base de datos. Sin embargo, los accesos a la base de datos no son la única razón por la que puede almacenar algo en caché. Por ejemplo, tomar texto de marcado sin formato (como lo que escribimos en los cuadros de preguntas / respuestas en StackExchange) y convertirlo a HTML puede llevar suficiente tiempo para que el resultado del almacenamiento en caché sea más eficiente.Si así fuera, el bucle interno de la plantilla podría verse más así:
Entonces, para reunir todo, volvamos a mis datos originales en la parte superior de esta respuesta. Si suponemos:
Bang3
acaba de actualizarseexpensive_markup_parser
)Entonces así es como se cargaría todo:
Foo
se recupera de la base de datosFoo.cache_key
(2014-05-16) no existe en el cachéFoo.todolists.all()
se consulta:Bar1
yBar2
se recuperan de la base de datosBar1.cache_key
(2014-05-10) ya existe en el caché ; recuperarlo y sacarloBar2.cache_key
(2014-05-16) no existe en el cachéBar2.todos.all()
se consulta:Bang3
yBang4
se recuperan de la base de datosBang3.cache_key
(2014-05-16) no existe en el caché{{ Bang3.body|expensive_markup_parser }}
se representaBang4.cache_key
(2014-04-01) ya existe en el caché ; recuperarlo y sacarloLos ahorros del caché en este pequeño ejemplo son:
Bar1.todos.all()
expensive_markup_parser
evitadas 3 veces:Bang1
,Bang2
yBang4
Y, por supuesto, la próxima vez que
Foo.cache_key
se vea , se encontrará, por lo que el único costo para renderizar es recuperarFoo
solo de la base de datos y consultar el caché.fuente
Su ejemplo es bueno si necesita un poco de recuperación o procesamiento de datos para cada comentario. Si solo toma el cuerpo y lo muestra, el caché será inútil. Pero puede almacenar en caché todos los árboles de comentarios (incluido {% para%}). En este caso, debe invalidarlo con cada comentario agregado, para que pueda colocar la marca de tiempo del último comentario o el recuento de comentarios en algún lugar en Publicar y construir una clave de caché de comentarios con él. Si prefiere datos más normalizados y usa comentarios en una sola página, puede borrar una clave de caché al guardar comentarios.
Para mí, guardar el recuento de comentarios en la publicación se ve lo suficientemente bueno (si no permite eliminar y editar comentarios): tiene un valor para mostrar en cualquier lugar con la publicación y una clave para el almacenamiento en caché.
fuente