El almacenamiento en caché es esencial para lograr un alto rendimiento y escalabilidad. Implementar la estrategia adecuada de almacenamiento en caché desde la fase de desarrollo es fundamental para evitar APIs rezagadas y tiempos de carga de páginas lentos. Laravel es uno de los frameworks PHP más populares, por lo que implementar la estrategia óptima de cache en Laravel es indispensable para una mejor experiencia de usuario y un mayor impacto en el negocio.

En este artículo, exploraremos estrategias para implementar y manipular el almacenamiento en caché en Laravel. Aprenderás cómo funciona la caché de Laravel, varias consultas de caché de Laravel y cómo puedes manejar la caché en las aplicaciones de Laravel.

Sacarás más provecho de este artículo si ya tienes un conocimiento básico de lo siguiente en tu haber:

Vamos a sumergirnos en el tema.

Mira Nuestra Guía en Video sobre la Caché de Laravel

¿Por qué es importante la caché?

Con el reciente auge de los negocios en Internet, diferentes empresas tienen estadísticas que muestran cómo el tiempo de carga del sitio web y el bajo rendimiento pueden impactar fuertemente en el SEO, el compromiso del usuario y las tasas de conversación sin el almacenamiento en caché. Y eso comienza con una excelente estrategia de almacenamiento en caché.

Según un estudio online, «1 segundo de retraso en la carga le costaría a Amazon 1.600 millones de dólares en ventas al año».

Otro estudio de Google informó: «Nuestra investigación muestra que si los resultados de la búsqueda se ralentizan aunque sea una fracción de segundo, la gente busca menos (en serio: Un retraso de 400 ms conlleva un descenso del 0,44% en el volumen de búsquedas, según los aficionados a los datos). Y esta impaciencia no se limita a las búsquedas: Cuatro de cada cinco usuarios de Internet hacen clic si un vídeo se detiene mientras se carga».

Una ligera lentitud en el tiempo de carga de tu página web puede tener un gran impacto en la experiencia de tus usuarios y en la pérdida de fondos en general.

¿Qué es la caché de Laravel?

Laravel proporciona una implementación robusta y fácil de usar de la caché y diferentes backends de caché. Con la caché de Laravel, se puede cambiar de manera eficiente y eficaz entre muchos motores de caché sin escribir ningún código.

Puedes encontrar la configuración de la caché de Laravel dentro de la carpeta config/cache.php, aunque es probable que solo necesites el archivo .env para cambiar entre diferentes backends de caché.

La caché de Laravel también proporciona muchos métodos prácticos que podemos utilizar para implementar diferentes estrategias de caché.

Controladores de caché de Laravel y comparaciones

La caché de Laravel proporciona grandes backends y controladores de caché. Dependiendo de tu caso de uso, puedes cambiar entre ellos para mejorar el rendimiento de tu aplicación y el tiempo de carga.

Dicho esto, la caché de Laravel también proporciona una manera perfecta de crear un backend personalizado y utilizarlo con la caché de Laravel, pero solo si la lista de abajo no se ajusta a tu caso de uso.

A continuación hablaremos de la lista de todos los backends que proporciona la caché de Laravel.

1. Archivo

El controlador de archivos es el backend por defecto utilizado por la caché de Laravel cuando no se especifica ningún controlador en el archivo .env.

El backend de archivos está diseñado para almacenar los datos en caché en un archivo encriptado que se encuentra en storage/framework/. Laravel crea un archivo encriptado con los datos y la clave de la caché cuando se almacenan nuevos datos. Lo mismo ocurre cuando el usuario intenta recuperar el contenido. Laravel cache busca en la carpeta la clave especificada y, si la encuentra, devuelve el contenido.

Aunque el backend de archivos funciona perfectamente y ahorra tiempo en la instalación y configuración de controladores externos, también puede ser perfecto para el desarrollo. Es más rápido que acceder a los datos desde el servidor de la base de datos directamente.

Para utilizar el controlador de archivos, añade el siguiente código a tu archivo .env:

CACHE_DRIVER=file

2. Array

El controlador de matrices es un backend de caché perfecto para ejecutar pruebas automatizadas y se configura fácilmente con Github Actions, Jenkins, etc.

El backend de array almacena los datos almacenados en caché en un array en PHP y no requiere que instales o configures ningún controlador. Funciona perfectamente para pruebas automatizadas, y es un poco más rápido que el backend de caché de archivos.

Para utilizar el controlador de matrices, añade el siguiente código a tu archivo .env:

CACHE_DRIVER=array

3. Base de datos

Cuando se utiliza el controlador de la base de datos, los datos se almacenan en la memoria para el proceso PHP actual. Por lo tanto, es necesario crear una tabla de base de datos para almacenar los datos en caché. Además, el almacenamiento en caché de la base de datos mejora la escalabilidad al distribuir la carga de trabajo de las consultas desde el backend a múltiples frontends.

Puedes ejecutar este comando de Artisan — php artisan cache:table — para autogenerar el esquema de la base de datos que necesita el controlador de la base de datos.

El controlador de la base de datos se utiliza principalmente en situaciones en las que se puede instalar cualquier software en la plataforma de alojamiento.

Por ejemplo, digamos que estás utilizando un plan de alojamiento gratuito con opciones limitadas. En ese caso, te sugerimos que te quedes con el controlador de archivos porque el controlador de la base de datos es, en la mayoría de los casos, el punto más débil de tu aplicación, y tratar de empujar más datos en ese cuello de botella no es una buena idea.

Para utilizar el controlador de la base de datos, añade el siguiente código a su archivo .env:

CACHE_DRIVER=database

4. Redis

El controlador redis utiliza la tecnología de caché basada en memoria llamada Redis. Aunque es rápido en comparación con los otros controladores de caché comentados anteriormente, requiere la instalación y configuración de tecnología externa.

Para utilizar el controlador de redis, añade el siguiente código a su archivo .env:

CACHE_DRIVER=redis

5. Memcached

Memcached es conocido por ser el almacén de caché basado en memoria más popular. Si no te importa un poco de mantenimiento extra del servidor (tener que instalar y mantener servicios adicionales), los controladores de caché basados en memoria Memcached son grandes opciones.

El uso del controlador memcached requiere la instalación del paquete Memcached PECL.

Para utilizar el controlador memcached, añade el siguiente código a su archivo .env.

CACHE_DRIVER=memcached 

El mejor controlador de caché a utilizar y el rendimiento del controlador de caché dependen del caso de uso de tu proyecto y de la cantidad de datos a recuperar.

Uso y métodos de la caché de Laravel

La caché de Laravel proporciona muchos métodos valiosos utilizados para implementar muchas estrategias de almacenamiento en caché.

A continuación, enumeraremos y explicaremos los diferentes métodos (clasificados según tu caso de uso):

  1. put()
  2. get()
  3. many()
  4. putMany()
  5. increment()
  6. decrement()
  7. forever()
  8. forget()
  9. flush()
  10. remember()
  11. rememberForever()

Almacenamiento de la caché

El almacenamiento de nuevos datos en la caché es muy sencillo utilizando los diferentes métodos, cada uno con varios casos de uso.

1. Cache::put()

Este método acepta tres parámetros clave, la duración y los datos que se van a almacenar en caché.

Veamos cómo utilizar Cache::put():

Cache::put(key, data, duration)

$post = Post::find(1);

Cache::put('post_1', $post, 20);

El código anterior almacenará en caché la entrada con la clave única durante 20 segundos.

2. Cache::putMany()

Este método almacena un array de datos en la caché a la vez con la misma duración. Acepta dos parámetros que son datos y segundos.

Veamos cómo utilizar Cache::putMany():

Cache::putMany(data, duration) // syntax

$posts = Post::all();

Cache::putMany($posts, 20);
3. Cache::remember()

Este método es otra excelente manera de implementar la estrategia de Aside de la caché. El método Cache::remember() acepta tres parámetros, una clave, unos segundos y un cierre utilizado para recuperar los datos de la base de datos si no se encuentran.

Veamos cómo utilizar Cache::remember():

Cache::remember(key, duration, closure) // syntax

Cache::remember('posts', 20, function(){
  return Post::all();
});

La caché de Laravel también tiene el método Cache::rememberForever(), que no acepta el parámetro seconds y almacena los datos para siempre.

4. Cache::forever()

Este método almacena los datos en el servidor de caché para siempre sin especificar ninguna duración. Puedes implementarlo con el siguiente código:

Cache::forever(key, data)

$post = Post::find(1);

Cache::forever('post_1', $post);

Recuperación de los datos de la caché

Los métodos de esta categoría recuperan datos de la caché. Algunos de estos métodos pueden comportarse de forma diferente dependiendo de si los datos se encuentran o no.

1. Cache::get()

Este método recupera datos del servidor de caché con una clave específica. Puedes recuperar un elemento utilizando el código siguiente:

Cache::get(key) // syntax

$posts = Cache::get('posts');
2. Cache::many()

Este método es similar a Cache::putMany(). Se utiliza para recuperar un array de datos de la caché de una sola vez utilizando un array de las claves de la caché. Puedes recuperar un array de caché usando el siguiente código:

Cache::many(keys) // syntax

const $keys = [
  'posts',
  'post_1',
  'post_2'
];

$posts = Cache::many($keys);
3. Cache::remember()

También puedes utilizar este método para recuperar los datos almacenados en la caché, comprobando el servidor de caché mediante la clave proporcionada. Si los datos están almacenados en la caché, los recuperará. En caso contrario, recuperará los datos del servidor de la base de datos y los almacenará en la caché. Este método es el mismo que el método Cache::rememberForever() con sólo un parámetro extra de segundos en el método Cache::remember().

Eliminación de elementos de la caché

Los métodos de esta categoría se utilizan para eliminar elementos de la caché, agrupados por funcionalidad.

1. Cache::forget()

Este método elimina un solo elemento de la caché con un parámetro clave especificado:

Cache::forget('key');
2. Cache::flush()

Este método borra todos los motores de la caché. Elimina todos los elementos almacenados en cualquier lugar de la caché:

Cache::flush();

Incremento o disminución de los valores de la caché

Puedes ajustar los valores de un valor entero almacenado en tu caché utilizando los métodos de incremento y decremento, respectivamente:

Cache::increment('key');

Cache::increment('key', $amount);

Cache::decrement('key');

Cache::decrement('key', $amount);

La caché de Laravel tiene muchos métodos excelentes que no hemos discutido anteriormente, pero los métodos anteriores son populares. Puedes obtener una visión general de todos los métodos en la documentación oficial de Laravel cache.

Explicación de los comandos de la caché

Laravel proporciona comandos para que trabajar con la caché de Laravel sea fácil y rápido. A continuación se muestra la lista de todos los comandos y sus funcionalidades.

Borrar la caché de Laravel

Este comando se utiliza para limpiar la caché de Laravel antes de que expire utilizando la terminal/consola. Por ejemplo, puedes ejecutar el siguiente comando:

php artisan cache:clear

Borrar el caché de la ruta

Este comando se utiliza para borrar la caché de rutas de tu aplicación Laravel. Por ejemplo, ejecuta el siguiente comando para borrar la caché de rutas:

php artisan config:cache

Borrar archivos de vista compilados

Este comando se utiliza para borrar los archivos de vista compilados de tu aplicación Laravel. Puedes lograrlo con el siguiente comando:

php artisan view:clear

Tabla de la base de datos

Cuando se utiliza el controlador de base de datos, es necesario crear un esquema de base de datos llamado caché para almacenar los datos de la caché. También puedes utilizar el comando Artisan para generar una migración con el esquema adecuado:

php artisan cache:table

Estrategias de caché de Laravel

Dependiendo del caso de uso de tu aplicación y de la estructura de los datos, es probable que tengas a tu disposición varias estrategias de caché diferentes. Incluso puedes crear una estrategia personalizada que se ajuste a tus necesidades. A continuación vamos a repasar la lista de estrategias de caché populares que puedes implementar en tu proyecto Laravel.

writeThrough

En la estrategia writeThrough, el servidor de caché se sitúa entre las peticiones y el servidor de base de datos, haciendo que cada operación de escritura pase por el servidor de caché antes de ir al servidor de base de datos. Por lo tanto, la estrategia de almacenamiento en caché writeThrough es similar a la estrategia readThrough.

Puedes implementar esta estrategia con la caché de Laravel con el siguiente código:

public function writeThrough($key, $data, $minutes) {
    $cacheData = Cache::put($key, $data, $minutes)

    // Database Server is called from(after) the Cache Server.
    $this->storeToDB($cachedData)
    return $cacheData
}

private function storeToDB($data){
    Database::create($data)
    return true
}

writeBack (writeBehind)

Esta estrategia es una forma más avanzada de implementar la estrategia writeThrough añadiendo retrasos en las operaciones de escritura.

También se puede llamar a esto la estrategia writeBehind por el retraso en el tiempo que se aplica al servidor de caché antes de escribir los datos en el servidor de la base de datos.

Puedes implementar esta estrategia con la caché de Laravel con el siguiente código:

$durationToFlush = 1; // (in minute)
 $tempDataToFlush = [];

  public function writeBack($key, $data, $minutes){
    return $this->writeThrough($key, $data, $minutes);
  }

  public function writeThrough($key, $data, $minutes) {
      $cacheData = Cache::put($key, $data, $minutes);
      $this->storeForUpdates($cacheData);
      return $cacheData;
  }

// Stores new data to temp Array for updating
  private function storeForUpdates($data){
    $tempData = {};
    $tempData['duration'] = this.getMinutesInMilli();
    $tempData['data'] = data;
    array_push($tempDataToFlush, data);
  }

// Converts minutes to millisecond
private function getMinutesInMilli(){
  $currentDate = now();
  $futureDate = Carbon(Carbon::now()->timestamp + $this->durationToFlush * 60000)
  return $futureDate->timestamp
}

// Calls to update the Database Server.
public function updateDatabaseServer(){
  if($this->tempDataToFlush){
    foreach($this->tempDataToFlush as $index => $obj){
      if($obj->duration timestamp){
        if(Database::create($obj->data)){
            array_splice($this->tempDataToFlush, $index, 1);
        }
      }
    }
  }
}

El método writeBack llama al método writeThrough, que almacena los datos en el servidor de caché y en una matriz temporal que se enviará posteriormente al servidor de bases de datos mediante el método updateDatabaseServer. Puedes configurar un CronJob para actualizar el servidor de la base de datos cada cinco minutos.

writeAround

Esta estrategia permite que todas las operaciones de escritura vayan directamente al servidor de la base de datos sin actualizar el servidor de caché – solo durante las operaciones de lectura se actualiza el servidor de caché.

Suponiendo que un usuario quiera crear un nuevo Artículo, éste se almacena directamente en el servidor de la base de datos. Cuando el usuario quiere leer el contenido del artículo por primera vez, el artículo se recupera del servidor de la base de datos y actualiza el servidor de caché para las siguientes solicitudes.

Puedes implementar esta estrategia con la caché de Laravel con el siguiente código:

public function writeAround($data) {
    $storedData = Database::create($data);
    return $storedData;
}

public function readOperation($key, $minutes){
    $cacheData = Cache::remember($key, $minutes, function() {
      return Article::all();
    })
    return $cacheData;
}

Cache Aside (Lazy Loading)

En esta estrategia, la base de datos se mantiene al margen y la aplicación solicita primero los datos al servidor de caché. Entonces, si hay un éxito (encontrado), los datos se devuelven al cliente. De lo contrario, si hay un fallo (no se encuentra), el servidor de la base de datos solicita los datos y actualiza el servidor de caché para las siguientes solicitudes.

Puedes implementar esta estrategia con la caché de Laravel con el siguiente código:

public function lazyLoadingStrategy($key, $minutes, $callback) {
  if (Cache::has($key)) {
      $data = Cache::get($key);
      return $data;
  } else {
      // Database Server is called outside the Cache Server.
      $data = $callback();
      Cache::set($key, $data, $minutes);
      return $data;
  }
}

El código anterior muestra la implementación de la estrategia de Aside de la caché, que equivale a implementar el método Cache::remember.

Read Through

Esta estrategia es directamente opuesta a la Estrategia de Caché Aside. En esta estrategia, el servidor de caché se sitúa entre la solicitud del cliente y el servidor de la base de datos.

Las solicitudes van directamente al servidor de caché, y éste se encarga de recuperar los datos del servidor de base de datos si no se encuentran en el servidor de caché.

Puedes implementar esta estrategia con la caché de Laravel con el siguiente código:

public function readThrough($key, $minutes) {
      $data = Cache::find($key, $minutes);
      return $data;
}

private function find($key, $minutes){
    if(Cache::has($key);){
      return Cache::get($key);
    }

    // Database Server is called from the Cache Server.
    $DBdata = Database::find($key);
    Cache:put($key, $DBdata, $minutes);
    return $DBdata;
}

¡Aquí lo tienes! Ahora hemos discutido algunas estrategias populares de almacenamiento en caché para tu próxima aplicación Laravel. Recuerda que incluso puedes utilizar una estrategia de almacenamiento en caché personalizada que se adapte mejor a los requisitos de tu proyecto.

Almacenamiento en caché de la parte de la interfaz de usuario de una aplicación Laravel

El almacenamiento en caché de la UI de nuestra aplicación Laravel es un concepto conocido como Full Page cache FPC. El término se refiere al proceso de almacenamiento en caché de la respuesta HTML de una aplicación.

Es excelente para aplicaciones en las que los datos HTML dinámicos no cambian con frecuencia. Puedes almacenar en caché la respuesta HTML para obtener una respuesta general y una renderización del HTML más rápidas.

Podemos implementar el FPC con la siguiente línea de código:

class ArticlesController extends Controller {
    public function index() {
        if ( Cache::has('articles_index') ) {
            return Cache::get('articles_index');
        } else {
            $news = News::all();
            $cachedData = view('articles.index')->with('articles', $news)->render();
            Cache::put('articles_index', $cachedData);                                         
            return $cachedData;           
        }  
    }
}

A primera vista, te habrás dado cuenta de que comprobamos si la página articles_index ya existe en nuestro servidor de caché. Luego devolvemos la página renderizándola con los métodos view() y render() de Laravel.

En caso contrario, se renderiza la página y se almacena el resultado en nuestro servidor de caché para posteriores peticiones antes de devolver la página renderizada al navegador.

Construir una aplicación Laravel

Ahora vamos a aplicar lo que hemos aprendido hasta ahora creando un nuevo proyecto Laravel e implementando la caché de Laravel.

Si no has utilizado Laravel, puedes leer qué es Laravel y echar un vistazo a nuestra lista de excelentes tutoriales de Laravel para empezar.

Configuración de Laravel

En primer lugar, vamos a crear una instancia nueva de Laravel utilizando el siguiente comando. Puedes consultar la documentación oficial para más información.

Abre tu consola y navega hasta el lugar donde almacena tus proyectos PHP antes de ejecutar los comandos siguientes. Asegúrate de tener Composer instalado y configurado correctamente.

composer create-project laravel/laravel fast-blog-app

// Change directory to current Laravel installation
cd fast-blog-app

// Start Laravel development server.
php artisan serve

Configurar y alimentar la base de datos

A continuación, configuraremos nuestra base de datos, crearemos un nuevo modelo de artículo y sembraremos 500 puntos de datos falsos para las pruebas.

Abre tu cliente de base de datos y crea una nueva base de datos. Haremos lo mismo con el nombre fast_blog_app_db y luego llenaremos nuestro archivo .env con las credenciales de la base de datos:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=fast_blog_app_db
DB_USERNAME=//DB USERNAME HERE
DB_PASSWORD=//DB PASSWORD HERE

A continuación, ejecutaremos el siguiente comando para crear la migración y el modelo de artículo simultáneamente:

php artisan make:model Article -m

Abre la migración recién creada encontrada database/migrations/xxx-create-articles-xxx.php y pega el siguiente código:

<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateArticlesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('articles', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->text('description');
            $table->timestamps();
        });
    }
    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('articles');
    }
}

A continuación, ejecuta el siguiente comando para crear una nueva seeder:

php artisan make:seeder ArticleSeeder

Abre el archivo de la seeder recién creada que se encuentra en database/seeders/ArticleSeeder.php y pega el siguiente código:

<?php
namespace Database\Seeders;
use App\Models\Article;
use Illuminate\Database\Seeder;
class ArticleSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        Article::factory()->count(500)->create();
    }
}

Abre el archivo DatabaseSeeder.php en el mismo directorio y añade el siguiente código:

<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
    /**
     * Seed the application's database.
     *
     * @return void
     */
    public function run()
    {
        $this->call(ArticleSeeder::class);
    }
}

A continuación, ejecuta el siguiente comando para crear una nueva factory:

php artisan make:factory ArticleFactory

Abre el archivo de factory recién construido que se encuentra en database/factories/ArticleFactory.php y pegue el siguiente código:

<?php
namespace Database\Factories;
use App\Models\Article;
use Illuminate\Database\Eloquent\Factories\Factory;
class ArticleFactory extends Factory
{
    /**
     * The name of the factory's corresponding model.
     *
     * @var string
     */
    protected $model = Article::class;
    /**
     * Define the model's default state.
     *
     * @return array
     */
    public function definition()
    {
        return [
            'title' => $this->faker->text(),
            'description' => $this->faker->paragraph(20)
        ];
    }
}

Ahora, ejecuta el siguiente comando para migrar nuestro esquema recién creado y también seed nuestros datos falsos para las pruebas:

php artisan migrate --seed

Creación del controlador de artículos

A continuación, crearemos nuestro controlador y configuraremos nuestras rutas para manejar nuestra solicitud y recuperar los datos utilizando el modelo anterior.

Ejecute el siguiente comando para crear un nuevo ArticlesController dentro de la carpeta app/Http/Controllers:

php artisan make:controller ArticlesController --resource

Abre el archivo y añade el siguiente código a la clase:

// Returns all 500 articles with Caching
public function index() {
  return Cache::remember('articles', 60, function () {
      return Article::all();
  });
}

// Returns all 500 without Caching 
public function allWithoutCache() {
  return Article::all();
}

Después de eso, abre el archivo api.php que se encuentra dentro de la carpeta routes/ y pega el siguiente código para crear un endpoint al que podamos llamar para recuperar nuestros datos:

Route::get('/articles', 'ArticlesController@index');

Route::get('/articles/withoutcache', 'ArticlesController@allWithoutcache');

Comprobación del rendimiento

Por último, probaremos el rendimiento de la respuesta de nuestra app con o sin la implementación de la caché de Laravel.

Esta captura de pantalla muestra el tiempo de respuesta de la API con la caché implementada:

Tiempo de respuesta de la API de Laravel con caché.
Tiempo de respuesta de la API de Laravel con caché.

La siguiente captura de pantalla muestra el tiempo de respuesta de la API sin la caché implementada – nótese que el tiempo de respuesta ha aumentado con respecto a la instancia en caché en más de un 5.000%:

Tiempo de respuesta de la API de Laravel sin caché.
Tiempo de respuesta de la API de Laravel sin caché.

Resumen

Hemos explorado varias estrategias para implementar y manipular el almacenamiento en caché de Laravel construyendo un nuevo proyecto, evaluando sus respuestas y comparando los resultados.

También has aprendido a utilizar los diferentes controladores y métodos de caché de Laravel. Además, hemos implementado diferentes estrategias de almacenamiento en caché para ayudarte a averiguar cuál puede ser la adecuada para ti.

Para saber más sobre Laravel, explora nuestra selección de los mejores tutoriales de Laravel. Tanto si eres un principiante como un desarrollador avanzado de Laravel, ¡hay algo para todos!

Si todavía tienes preguntas sobre el almacenamiento en caché de Laravel, por favor háznoslo saber en la sección de comentarios.

Solomon Eseme

I am a Software Engineer and Content Creator who is geared toward building high-performing and innovative products following best practices and industry standards. I also love writing about it at Masteringbackend.com. Follow me on Twitter, LinkedIn, and About Me