Pautas sobre el manejo de cache

LIBP-0119 (Libro de pautas)

La cache es un concepto fundamental en el desarrollo. Dentro de la complejidad del desarrollo en PHP hay conceptos muy interesantes que pueden mejorarse si se integra un sistema de cache. En el desarrollo en PHP normalmente nos interesará guardar en la cache:

  • Una página completa para no volver a generarla
  • Trozos estáticos de webs semi-dinámicas
  • Resultados de consultas a BBDD u otros datos
  • El código ya compilado para no volver a procesarlo

Pautas

TítuloCarácter
Uso de la técnica de cachear páginasObligatoria
Asegurar que el identificador de cache sea único Obligatoria
Elegir un tiempo de expiracion de la cache que sea optimoObligatoria
Emplea cache de código intermedioObligatoria

Uso de la técnica de cachear páginas

Es recomendable cachear paginas completas para facilitar que el servidor pueda devolver páginas estáticas de forma sencilla

El motor de PHP es uno de los más rápidos generando páginas web aún así nunca será tan rápido como no tener que generar nada y que el servidor web devuelva páginas estáticas. A pesar de disponer de una aplicación web escrita en PHP se puede conseguir la misma velocidad que en una web estática cacheando las páginas generadas. Lo que se propone a continuación es un sistema de cache donde las páginas sólo se generarán la primera vez que se acceda a ellas, en siguientes peticiones el servidor web devolverá las páginas estáticas generada

Para hacer cache de páginas utilizaremos dos archivos cache.start.php que verificará si existe una copia en cache de la pagina en cuyo caso lo mostrará al usuario y cache.end.php que se encargará de crear el cache de la página seleccionada.

<?php
      // Settings
      $cachedir = 'cache/';   // directorio de cache
      $cachetime = 86400;   // duración del cache
      $cacheext = 'cache';   // extensión de cache

      // script a procesar
      $cachepage = $_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
      $cachefile = $cachedir.md5($cachepage).'.'.$cacheext;

// Calculamos el tiempo de cache
      if (@file_exists($cachefile)) {
          $cachelast = @filemtime($cachefile);
      } else {
          $cachelast = 0;
      }
      @clearstatcache();

      // Mostramos el archivo si aun no vence
      if (time() - $cachetime <$cachelast) {
          @readfile($cachefile);
          exit();
      }
      ob_start();
      ?>

El siguiente paso es crear el archivo cache.end.php, este archivo se ejecutará si y solo así el script llega al final, lo cual indica que aun no tiene una copia en cache, para ello se crea el archivo y dentro del mismo se escribe el contenido del buffer como en el ejemplo.

// Generamos el nuevo archivo cache
      $fp = @fopen($cachefile, 'w');

      // guardamos el contenido del buffer
      @fwrite($fp, ob_get_contents());
      @fclose($fp);
      ob_end_flush();

Para utilizar este métodos solo es encesario crear una carpeta donde se almacenan los archivos de cache e incluir los ficheros dentro de cada script que se quiere procesar. De esta manera incluimos cache.start.php al inicio y cache.end.php al final del script a procesar.

<?php include("cache.start.php"); ?>
      <html>
      ...
      </html>
      <?php include("cache.end.php"); ?>

Asegurar que el identificador de cache sea único

Es necesario asegurar que el identificador asociado a la entrada es cache es único para facilitar la coherencia de contenidos. La mejor manera es centralizar el almacenamiento en cache

Cuando el sistema de cache comprueba si un resultado ya existe en cache, se utiliza el identificador único para comprobarlo. Es muy importante asegurarse de que sus identificadores son realmente únicos, de lo contrario tendrá dos temas por separado utilizando la misma caché y serán contradictorias entre sí.

La mejor manera de asegurar esto es tener el código de almacenamiento en caché para ese identificador sólo una vez en el código, por ejemplo, en una función o método, y realizar llamadas al mismo desde múltiples lugares si es necesario.

Elegir un tiempo de expiracion de la cache que sea optimo

En función de los factores críticos de sus datos, establezca un plazo de expiración sobre los elementos que se almacenan en cache

Una de los mayores dificultades del almacenamiento en caché es elegir el tiempo de caducidad de los elementos que se almacenan. Es recomendable configurar la cache asignando los diferentes plazos de expiración de cada elemento. La elección de un tiempo de caducidad tiene muchos factores que considerar como el tipo de datos que se almacenan, la frecuencia de los cambios en los datos, la cantidad de trafico esperada...

Si los cambios son muy frecuentes, es lógico establecer el tiempo de la expiracion de cache en torno a cinco minutos. A lo sumo, los datos sólo serán cinco minutos validez. Si usted tiene una web de alto tráfico, este esquema seguirá ofreciendo una mejora de rendimiento considerable, ya que puede recibir cientos de peticiones en cinco minutos.

Si tiene datos que no cambian con frecuencia, como los detalles del producto, puede desea establecer el caché mucho mas amplia, de días e incluso semanas. En algunas situaciones, donde la tarea de carga tarda mucho tiempo, puede haber una segunda solicitud de información antes de la primera solicitud se ha completado y ha almacenado el resultado en caché. En este caso, la tarea de carga se ejecutará de nuevo para el segundo En situaciones de trafico alto, esto puede llevar a su sitio a una paralización total cada vez que el caché expire. Para evitar esto, usted debe ejecutar la tarea de carga y reemplazar los datos en la memoria caché cuando cambian los datos, y establecer la memoria caché, de por vida a los oficiales administrativos nulos de manera que sea siempre válido. Esto asegurará que los datos siempre se lee de la caché cuando es necesario, y la tarea de carga sólo se ejecuta cuando se desea.

Hay algunos datos que puede almacenar en caché durante un largo periodo de tiempo, tales como la salida de una función matemática intensiva, donde la misma entrada siempre se producirá el misma salida. En este caso, se puede establecer el vencimiento a 365 días o más tiempo.

Emplea cache de código intermedio

Utilizar alguna herramienta que proporcione cacheado de OPCODE, es recomendable utilizar APC

Realizar la compilación del código de PHP en el servido consume tiempo, dado las siguientes características:

  • El código de PHP es recompilado cada vez que hay una petición
  • Compilar algunos archivos de PHP toma más tiempo que ejecutarlos, en particular si estos incluyen muchos otros archivos (clases, configuración, etc.)

En esta fase, los archivos con código PHP normalmente no sufren modificaciones por lo que es recomendable utilizar un software especializado que ofrezca la posibilidad de cachear el código intermedio( es decir el código PHP cuya sintaxis ya ha sido revisada y se han eliminado los fragmentos innecesarios como comentarios), de manera que se simplifica el trabajo al compilador. Cada vez que se carga un archivo PHP, parte del trabajo ya esta realizado.

Normalmente los caches de código intermedio leen datos de los archivos PHP y del dispositivo donde esta almacenado para crear un identificador único para el mismo y así evitar que un mismo archivo sea leído varias veces en una misma petición. De esta manera se consiguen las siguientes mejoras:

  • Se elimina la etapa de parseo del código intermedio PHP.
  • Se reduce el consumo de recursos de disco duro y optimizaciones al código intermedio.
  • Las aplicaciones que normalmente realicen peticiones cortas con una inclusión moderada de otros archivos en la misma pueden obtener mejoras hasta del 200% y 300%.
  • Para procesos largos la optimización puede ser del 30% al 40%. En cualquier caso la mejora siempre es considerable.

La mayor ventaja de los cacheadores de código intermedio es que no requieren que la aplicación sea desarrollada bajo ningún tipo de estándar o con algún requerimiento especial. La única limitación es que PHP debe estar instalado como un módulo del servidor o usando FastCGI, esto se debe a que los datos del código intermedio se almacenan en memoria compartida y ésta no está disponible para módulos cgi.