Sistema de caché para optimizar web PHP

Uno de los factores por los cuales deberíamos preocuparnos en nuestro día a día es tener un buen tiempo de respuesta en nuestras aplicaciones, este se puede ver degradado por varios factores, pero por fortuna para nosotros, existen muchos componentes y procedimientos que nos pueden ayudar a optimizarlo. En este blog te compartiré una serie de pasos relacionados con el caché, que pueden ayudarte a mejorar bastante los tiempos de respuesta.


¿Qué es Caché?


El caché es un componente que nos permite almacenar en memoria datos previamente procesados, regularmente los sistemas de caché funcionan como un Hashmaps o diccionario clave-valor, en dónde la clave es una cadena de texto que funciona como identificador y el valor puede contener casi cualquier cosa.


En general el concepto de caché se traduce a lo siguiente:



En dónde:

  1. El cliente envía una petición al servidor.
  2. Si la petición ya se encuentra en caché, entonces regresa la respuesta guardada.
  3. Si la petición no se encuentra en caché, entonces es enviada a nuestra aplicación para que sea procesada.
  4. La aplicación procesa los datos y crea una respuesta.
  5. La aplicación almacena la respuesta en caché
  6. La respuesta es enviada al cliente.


La memoria caché está limitada a valores computables, sin embargo, existen herramientas y técnicas que nos pueden ayudar a emular un sistema de caché para archivos físicos e inclusive para manejar bytecode, a continuación hablaremos de ellas.


¿Cuándo debería usar caché?


La respuesta a esta pregunta es casi siempre, un sistema de caché mejorará de manera increíble los tiempos de respuesta de tu aplicación, inclusive si tienes contenido generado dinámicamente puedes ahorrar unos milisegundos o segundos dependiendo de la complejidad y configuración que utilices.


¿Cuándo no usar caché?


El único enemigo real del caché son los sistemas real-time, en dónde se tienen actualizaciones por milisegundos, como lo puede ser un sistema de seguimiento de vehículos a través de GPS. De todos modos, se pueden utilizar ciertas técnicas de caché para agilizar parte del proceso completo.


Invalidar caché


Dado que el caché almacena datos/archivos procesados, es importante hacer limpieza de este cada cierto tiempo, haciendo que nuestra aplicación no se quede obsoleta en cuanto información y liberando memoria, el término "invalidar" hace referencia a eliminar los datos que se encuentran en caché.


Existen 3 factores que provocan la invalidación de caché:


Invalidación por tiempos definidos


Cuando almacenamos datos en caché, tenemos la posibilidad de elegir un tiempo que se mantendrá guardado, al pasar este tiempo, el caché será invalidado automáticamente.


El tiempo que debemos mantener los datos en caché dependerá de la concurrencia de la aplicación o fragmento de código, si es un código que regresa siempre la misma salida, por ejemplo, una landing page, podemos establecer un periodo de 30 mins a 2 horas, sin embargo, si el código que queremos cachear está en constante cambio, podemos incluso hacer caché de 3 a 20 segundos, en dónde no parece de mucha ayuda, pero en escenarios concurridos puede mejorar muchísimo los tiempos de respuesta.


Invalidación por elementos poco utilizados


Los elementos de caché que se utilizan en menor frecuencia también puede ser invalidados, en especial si la memoria está por agotarse, es muy probable que el propio sistema de caché invalide estos datos de manera automática.


Invalidación manual


Al igual que tenemos el control sobre lo que se almacena en caché, podemos definir en qué momento invalidar, esto resulta útil cuando, por ejemplo, cambiamos el estado de la base de datos y necesitamos que el caché obtenga las nuevas entradas.


Cacheando variables


Si lo que deseamos es almacenar valores diversos, siendo éstos, variables de sesión, resultados de queries, etc, entonces, necesitaremos de algún motor para manejar el caché, las principales opciones que tenemos en PHP son memcached y redis.


En ambos casos tenemos una API bastante amplia que nos permitirán almacenar valores en caché en una estructura clave-valor, ambos cuentan con la opción de establecer el tiempo que permanecerán los datos en caché.


Dado que ambas herramientas son muy buenas, tienden a tener muy buena integración con frameworks, por lo que muy probablemente ya tengas pre-configurado una librería en la herramienta que utilizas día a día.


La instalación de un servidor para procesar caché no es complicada y brinda muchísimos beneficios.


Cacheando bytecode


A pesar de que php es un lenguaje interpretado, es posible guardar fragmentos de código que siempre regresan la misma salida, haciendo que su ejecución sea mucho más rápida.


Para lograr ésto, existe la extensión OPCache, disponible a partir de php 5.5, su trabajo es análizar todos esos fragmentos de código y de manera automática los irá gestionando.


La configuración de la extensión se hace en el archivo php.ini y se recomienda una configuración inicial como la siguiente:


 
opcache.memory_consumption=128  
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=4000
opcache.revalidate_freq=60
opcache.fast_shutdown=1 opcache.enable_cli=1


En dónde:

  • memory_consumption:Indica el tamaño de la memoria en MB que puede ser utilizada por OPCache, el valor mínimo permitido es de 8MB.
  • interned_strings_buffer: Indica la cantidad de memoria en MB para almacenar cadenas de téxto.
  • max_accelerated_files: Indica el número máximo de scripts que pueden ser almacenados en OPCache.
  • revalidate_freq: Tiempo en segundos que se mantendrán cacheados los scripts.
  • fast_shutdown: Permite deshabilitar el sistema de caché de manera rápida, delegando la limpieza de memoria al engine Zend. En versiones de php 7.2 o mayor, esta configuración no es necesaria.
  • enable_cli: Habilita el caché para la versión CLI de php.


El resto de configuraciones son opcionales, sin embargo, te comparto la documentación oficial de OPCache por si deseas saber más al respecto.


Cacheando archivos


Una técnica muy utilizada especialmente en herramientas que pre-procesan archivos, es mantener una versión cacheada del archivo previamente procesado, siguiendo el mismo concepto inicial, si el archivo ya fue procesado, se regresa como respuesta, de lo contrario, se compila, se guarda físicamente en una carpeta y regresa el archivo.

Una ventaja que tenemos es que podemos combinar partes físicas, con datos previamente guardados en memcached o redis teniendo una mezcla entre contenido estático y dinámico, pero cargado en memoria y con la posibilidad de manejar diferentes tiempos de vida.


Cacheando respuestas


La manera de cachear respuestas para ser servidas a mayor velocidad se da a través de cabeceras en la petición/respuesta, éstas pueden ser gestionadas con ayuda de tu servidor http como apache o nginx y suelen trabajar muy bien con librerías como cache-plugin.


Las cabeceras que suelen ser utilizadas para el control de caché son:

  • Expires: Tiempo de expiración del caché.
  • Cache-Control: Directivas que indican el comportamiento del caché.
  • ETag: Hash del contenido, se utiliza cuando se generan datos dinámicamente.
  • Last-Modified: Última fecha de modificación.
  • If-None-Match: Cabecera que indica que el contenido no se encuentra en caché.
  • If-Modified-Since: Cabecera que indica que el contenido que encuentra en caché ha sido actualizado.


Conclusión:


El caché es el mejor aliado que tenemos para optimizar los tiempos de respuesta de nuestra aplicaciones, es importante que dediquemos un tiempo a comprender y experimentar respecto al uso de caché.


Si es que utilizas algún framework, te invito a que investigues que métodos de cahé implementa y lo compartas en los comentarios o si tienes alguna otra implementación que nos pueda ayudar, sería genial que la compartieras.


Espero que esta guía te haya sido de ayuda.


¡Hasta la próxima!