php elephant sticker
Photo by RealToughCandy.com on Pexels.com
目次

[Guía práctica completa] Estrategia de caché en Laravel — Cache/Redis/Tags, Locks, caché HTTP, optimización de despliegue y UI rápida y accesible

Lo que aprenderás en este artículo (puntos clave)

  • Cómo utilizar las funciones de caché de Laravel como una estrategia de diseño y no de forma improvisada
  • Patrones prácticos para Cache::remember(), tags, locks, expiración y estrategias de invalidación
  • Cómo elegir entre Redis / database / file y qué tener en cuenta en entornos con múltiples servidores
  • Cómo incorporar config:cache, route:cache, view:cache y event:cache en despliegues de producción
  • Caché HTTP, ETag, CDN, optimización de imágenes y listas, y cómo manejar la frescura del contenido
  • Diseño de claves, monitoreo, pruebas y estados de carga accesibles para evitar incidentes relacionados con la caché

Lectores objetivo

  • Ingenieros Laravel de nivel principiante a intermedio: quienes ya tienen páginas funcionando, pero cuyas listas y dashboards se están volviendo más lentos
  • Tech leads: quienes quieren estandarizar el diseño de caché basado en Redis dentro de un equipo
  • Equipos de QA / mantenimiento: quienes quieren reducir problemas como “aparecen pantallas antiguas” o “solo se actualiza una parte de la página” causados por la caché
  • Diseñadores / especialistas en accesibilidad: quienes quieren crear interfaces que no solo sean rápidas, sino que también comuniquen claramente los estados de espera y actualización

Nivel de accesibilidad: ★★★★★
La caché puede parecer un tema de rendimiento, pero en la práctica afecta directamente a “cómo se muestra el tiempo de espera” y “cómo se comunican las actualizaciones”. En este artículo también cubriremos role="status", aria-busy, indicadores de progreso que no dependan solo del color, auto-refresh que no sea demasiado agresivo y diseño de listas que no deje desorientados a los usuarios de teclado.


1. Introducción: la caché no es “magia que hace todo más rápido”, sino un diseño para reducir recomputaciones

A medida que continúas desarrollando con Laravel, las páginas que al principio eran rápidas suelen volverse más pesadas con el tiempo. Aumentan las condiciones de búsqueda, los dashboards agregan más totales, la navegación empieza a mostrar contadores y se añaden APIs externas y procesamiento de archivos. Una respuesta muy común en este punto es: “Por ahora, pongamos simplemente un remember()”. Por supuesto, eso puede hacer que algo sea más rápido temporalmente. Pero si sigues añadiendo caché sin diseñar la expiración y la invalidación, aparece otro conjunto de problemas: “se muestra información antigua”, “no sabemos qué limpiar” y “solo fallan en actualizarse algunas partes”.

La caché es conveniente, pero cuanto más casualmente se añade, más difícil se vuelve de gestionar. Lo importante es decidir primero qué almacenar en caché, durante cuánto tiempo, en qué unidad y cuándo limpiarlo. Laravel proporciona una API de Cache unificada, por lo que aunque cambies de driver más tarde, la forma de uso apenas cambia. Eso significa que, si organizas bien el razonamiento desde el principio, pasar a Redis o escalar a un entorno con múltiples servidores será mucho más fácil después.


2. Primero, organiza el problema: se vuelve más fácil si divides los objetivos de caché en cuatro tipos

En la práctica, ayuda dividir los objetivos de caché en las siguientes cuatro categorías.

2.1 Datos para la visualización en pantalla

  • Rankings de la página principal
  • Listas por categoría
  • Valores agregados del dashboard
  • Contadores en la barra lateral

Se trata de información donde “estar un poco desactualizada no es desastroso, pero la velocidad importa”. Es el lugar más fácil para empezar.

2.2 Cálculos costosos

  • Totales de ventas diarias
  • Rankings de artículos populares
  • Valores acumulados por cliente
  • Respuestas preformateadas de APIs externas

Este tipo se vuelve caro si se recalcula en cada request, así que el efecto de la caché es grande. Al mismo tiempo, también es un área donde pueden quedarse “números desactualizados” si el diseño de actualización no se considera cuidadosamente.

2.3 Locks para evitar contención

  • Prevenir ejecuciones duplicadas del mismo job
  • Prevenir lanzamientos duplicados de un proceso batch
  • Prevenir la emisión duplicada de la misma factura

Aquí no se trata principalmente de velocidad, sino de consistencia y seguridad. Aquí es donde entran en juego los atomic locks de Laravel.

2.4 Caché para optimización de despliegue

  • config cache
  • route cache
  • view cache
  • event cache

No es caché de datos de la aplicación, sino caché que acelera el propio arranque y routing de Laravel. Es muy importante en producción, pero de hecho puede estorbar durante el desarrollo local.

Una vez que tienes estas cuatro categorías en mente, se vuelve más fácil pensar: “esta caché es para velocidad de visualización”, “esta es para exclusión mutua” o “esta es para optimización de despliegue”.


3. La API de Cache de Laravel: empieza entendiendo remember como el núcleo

La API de caché de Laravel es muy sencilla. Incluso con solo entender put, get y remember puedes llegar bastante lejos.

use Illuminate\Support\Facades\Cache;

$popularPosts = Cache::remember('home:popular-posts', 300, function () {
    return Post::query()
        ->published()
        ->orderByDesc('views_count')
        ->take(10)
        ->get(['id', 'title', 'slug', 'views_count']);
});

En este ejemplo, las publicaciones populares se almacenan bajo la clave home:popular-posts durante cinco minutos.
Lo importante aquí es que el nombre de la clave y el tiempo de expiración son en sí mismos información de diseño.

  • Si el nombre de la clave es ambiguo, después será difícil limpiarla
  • Si la expiración es ambigua, acabarás teniendo datos demasiado antiguos o recomputaciones demasiado frecuentes

Por eso ayuda pensar en los nombres así desde el principio:

  • Nombre de pantalla
  • Objetivo
  • Condiciones
  • Locale o tenant ID si hace falta

Ejemplos:

  • home:popular-posts
  • dashboard:tenant:12:daily-sales
  • products:list:ja:category-books:page-1

Si haces esto, el propósito seguirá siendo obvio incluso durante la operación.


4. Elegir un driver: empieza preguntando si es compartido

Laravel puede manejar múltiples drivers de caché mediante una API unificada. En la práctica, la siguiente forma de pensar resulta útil.

4.1 file

  • Fácil para desarrollo local
  • Puede ser aceptable en un solo servidor
  • No se comparte en configuraciones multi-servidor, así que hay que tener cuidado

4.2 database

  • Fácil de introducir
  • Encaja bien con la configuración inicial de Laravel
  • Puede no escalar bien para accesos de alta frecuencia u operaciones a gran escala

4.3 redis

  • Funciona bien en entornos multi-servidor
  • Fácil de combinar con locks, queues y sessions
  • En la práctica, suele ser la opción más fácil y confiable

4.4 memcached

  • Una opción rápida
  • Fuerte si tu equipo ya tiene experiencia operándolo
  • Pero en proyectos Laravel, Redis suele ser más fácil de adoptar

Para sistemas de producción pequeños a medianos, usar Redis como supuesto por defecto tiende a hacer el diseño más estable. Sessions, queues y locks también pueden consolidarse alrededor de Redis, lo que hace más fácil razonar sobre escalado multi-servidor y procesamiento asíncrono con un almacenamiento compartido ya establecido.


5. Diseño de claves: muchos incidentes de caché empiezan con nombres vagos

La parte más subestimada pero altamente efectiva del diseño de caché es el diseño de claves. Las principales cosas que deberían entrar en una clave son:

  • Pantalla o propósito
  • Si depende del usuario
  • Si depende del tenant
  • Locale
  • Condiciones de búsqueda
  • Número de página
  • Versión

Por ejemplo, el resultado de búsqueda de una pantalla de listas necesita información como esta:

$key = sprintf(
    'users:index:tenant:%d:q:%s:status:%s:page:%d',
    tenant()->id,
    md5($search),
    $status ?: 'all',
    $page
);

Si pones el término de búsqueda en bruto directamente en la clave, puede hacerse demasiado largo, así que usar algo como md5() para acortarlo puede ser una buena opción.
Además, si una página tiene variantes por idioma y no incluyes el locale en la clave, la versión en japonés y la versión en inglés pueden mezclarse.
Un incidente de caché no es solo “se muestran datos antiguos”; también puede convertirse en “se muestra información de otro usuario”. Por eso, la separación específica por usuario y por tenant debe manejarse cuidadosamente.


6. Cómo decidir el TTL: demasiado corto y demasiado largo, ambos causan problemas

Si decides la expiración de la caché solo por intuición, suele fallar. Un método práctico es determinarla según la naturaleza del objetivo.

6.1 Corto (30 segundos a 5 minutos)

  • Resultados de búsqueda
  • Contadores del dashboard
  • Respuestas de APIs externas
  • Información resumida en pantallas de listas

6.2 Medio (10 minutos a 1 hora)

  • Rankings populares
  • Listas por categoría
  • Agregados que cambian con frecuencia, pero no requieren frescura instantánea

6.3 Largo (medio día a un día)

  • Datos maestros
  • Valores de configuración
  • Clasificaciones fijas como listas de prefecturas

Si el TTL es demasiado corto, acabarás recomputando casi cada vez aunque “estés usando caché”. Si es demasiado largo, la actualización se reflejará con lentitud.
Cuando tengas dudas, suele ser más fácil decidir preguntando: “¿Es aceptable que esto esté un poco desactualizado?”
Además, si combinas TTL con limpieza explícita al actualizar, incluso TTL más largos pueden operarse de forma segura.


7. Estrategia de invalidación: decide de antemano dónde debe llamarse forget()

La caché es más difícil de limpiar que de añadir. En la práctica, ayuda pensar en estos tres patrones.

7.1 Solo TTL

Este enfoque deja que los datos expiren de forma natural después de unos minutos y no limpia explícitamente al actualizar.
Funciona bien para listas de búsqueda o dashboards ligeros.

7.2 forget() al actualizar

Si el “momento del cambio” está claro —como publicar un artículo, actualizar un usuario o cambiar una configuración— limpia inmediatamente después del proceso de actualización.

$post->update($data);

Cache::forget('home:popular-posts');
Cache::forget("post:{$post->id}:detail");

7.3 Limpiar por grupos con tags

Esto es útil cuando quieres limpiar de una vez todas las cachés relacionadas.
Sin embargo, como depende del soporte del driver y de las condiciones operativas, es más seguro definir dentro del equipo en qué lugares se permite usar tags.

Cache::tags(['posts'])->put("post:{$post->id}", $post, 3600);
Cache::tags(['posts'])->flush();

Para pantallas que se actualizan con frecuencia, “solo TTL” suele ser suficiente. Si añades invalidación estricta en todas partes, la complejidad aumenta rápidamente, así que es práctico equilibrarlo según importancia y frecuencia de actualización.


8. Locks: la caché de Laravel también puede usarse para control de concurrencia

La caché puede utilizarse no solo para velocidad de visualización, sino también para prevenir ejecuciones duplicadas. Laravel tiene una API de locks que te permite asegurar que el mismo proceso no se ejecute simultáneamente.

use Illuminate\Support\Facades\Cache;

$lock = Cache::lock('billing:issue:2026-04', 120);

if ($lock->get()) {
    try {
        // Proceso de emisión de facturas
    } finally {
        $lock->release();
    }
}

Esto es útil en situaciones como:

  • Prevenir la emisión duplicada de facturas mensuales
  • Prevenir lanzamientos duplicados de la misma exportación CSV
  • Prevenir la ejecución concurrente del mismo proceso batch
  • Complementar la protección contra doble envío debido a pulsaciones repetidas del botón

Esto se vuelve especialmente efectivo cuando se combina con el Scheduler o con Queue. Los locks no son llamativos, pero son extremadamente valiosos en sistemas reales.


9. Operación multi-servidor: si te quedas en file cache, es probable que haya inconsistencias

Si solo tienes un servidor en producción, los problemas pueden permanecer ocultos. Pero en cuanto tienes dos o más, los diseños basados en file cache o almacenamiento local se rompen rápidamente.
Por ejemplo, el servidor A puede limpiar la caché tras una actualización, mientras que el servidor B sigue conservando la caché antigua.
Por eso, en configuraciones multi-servidor, es más seguro tener presentes los siguientes puntos:

  • Mover la caché a un almacenamiento compartido como Redis
  • Mover también las sesiones a un almacenamiento compartido
  • Usar el mismo almacenamiento compartido para los locks
  • Ejecutar la limpieza de caché de forma consistente en todos los servidores durante el despliegue

Un diseño que parece seguro con “ahora mismo solo tenemos un servidor” puede convertirse en un gran obstáculo cuando escales después. No necesitas un gran sistema desde el primer día, pero sí conviene diseñar de una forma que sea fácil de migrar hacia infraestructura compartida.


10. Optimización de despliegue: incorpora config / route / view / event caches

Laravel también incluye optimizaciones integradas basadas en caché para producción. En la práctica, es importante incorporarlas en los procedimientos de despliegue.

10.1 config cache

Esto consolida los archivos de configuración, reduciendo las lecturas de archivos al arrancar.
Es especialmente efectivo en producción.
Sin embargo, si tu implementación llama directamente a env() fuera de los archivos de configuración, esto puede crear inconsistencias.

10.2 route cache

Suele ayudar en aplicaciones grandes con muchas definiciones de rutas.
Si todavía tienes rutas basadas en closures, la adopción se vuelve más difícil, así que una estructura de rutas limpia ayuda.

10.3 view cache

Precompilar vistas Blade reduce la espera en el primer acceso.

10.4 event cache

Esto acelera la resolución de listeners de eventos. Es especialmente útil en apps que dependen mucho de un diseño orientado a eventos.

Una secuencia típica de despliegue se ve así:

php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan event:cache

Como el desarrollo local cambia frecuentemente, no hace falta usar siempre esto allí. Lo importante es separar claramente el uso local del uso en producción.


11. optimize y optimize:clear: alinea el significado en la operación

Laravel también proporciona comandos relacionados con optimize.
Una fuente común de confusión dentro de los equipos es no compartir qué comando cachea qué y cuál limpia qué.
Una buena práctica es documentar los comandos relacionados con caché usados durante el despliegue y el procedimiento de limpieza usado durante incidentes.

Por ejemplo:

  • Durante el despliegue: config:cache, route:cache, view:cache, event:cache
  • Durante emergencias: optimize:clear o comandos individuales *:clear

Esto deja mucho más claro dónde mirar cuando, por ejemplo, “un cambio de configuración en producción no se refleja”.


12. Caché HTTP: no te apoyes solo en la caché de aplicación, usa también el navegador y el CDN

La caché de aplicación es importante, pero la caché a nivel HTTP también lo es muchísimo.
Especialmente para páginas de listas, imágenes y páginas públicas, suele ser más eficiente dejar que el navegador y el CDN hagan el trabajo.

12.1 ETag y 304

Si el contenido no ha cambiado, devuelve 304 Not Modified sin enviar el body otra vez.
Esto funciona bien para APIs y algunas respuestas de listas.

12.2 Cache-Control

Para archivos públicos que rara vez cambian, puedes cachearlos durante mucho tiempo.
Por otro lado, ten cuidado de no aplicar caché fuerte a páginas que varían por usuario.

12.3 Assets con hash

Si las imágenes y los assets compilados incluyen hashes en sus nombres de archivo, pueden cachearse durante mucho tiempo.
Cuando el contenido cambia, también cambia la URL, reduciendo los problemas de caché obsoleta.

La caché HTTP tiene un propósito ligeramente distinto al de la caché interna de Laravel.
La caché interna trata de reducir la recomputación, mientras que la caché HTTP trata de reducir la retransmisión.


13. Respuestas de APIs externas: la caché de corta duración y el fallback son prácticos

Las APIs externas deberían diseñarse bajo el supuesto de que son lentas, fallan y tienen rate limits.
Aquí es donde la caché de corta duración se vuelve útil.

$data = Cache::remember('weather:tokyo', 300, function () {
    return Http::timeout(10)->get('https://example.com/api/weather/tokyo')->json();
});

Incluso conservar el resultado durante cinco minutos puede reducir significativamente la carga y la tasa de fallos.
En la práctica, la siguiente política suele ser útil:

  • En éxito: guardar el nuevo valor
  • En fallo: si existe un valor reciente en caché, devolverlo
  • Si solo quedan datos muy antiguos: decir claramente al usuario “actualmente no se puede actualizar”

En otras palabras, la caché es útil no solo para velocidad, sino también como buffer frente a fallos de APIs externas.


14. Dashboards y listas: equilibra la caché con la accesibilidad

La caché reduce el tiempo de espera. Pero aun así, si el momento de actualización y la comunicación del estado no se manejan con cuidado, los usuarios pueden confundirse. Los siguientes puntos son especialmente importantes.

14.1 Comunicar la carga

<section aria-busy="true" id="summary">
    <p class="sr-only">Cargando datos de resumen.</p>
</section>

14.2 Comunicar los resultados actualizados

<div role="status" aria-live="polite" class="text-sm">
    Se encontraron 24 resultados.
</div>

14.3 No depender solo del color

Los aumentos, disminuciones y estados deben comunicarse con palabras, no solo con rojo y verde.
Ejemplos:

  • Las ventas aumentaron
  • Poco stock
  • La actualización falló

14.4 No hacer el auto-refresh demasiado agresivo

Que la caché facilite las actualizaciones no significa que la pantalla deba reescribirse sola cada pocos segundos. Eso dificulta que los usuarios interactúen con calma.
En muchos casos, es más seguro mantener el auto-refresh al mínimo y también ofrecer un botón de “Actualizar datos más recientes”.


15. Testing: asegúrate de que el comportamiento relacionado con caché no se rompa

La caché es útil, pero sin pruebas es fácil pasar por alto problemas como “la página se hizo más rápida, pero ahora aparecen valores antiguos”.
Como mínimo, los siguientes puntos de vista son efectivos.

15.1 Confirmar que los datos se almacenan en caché

Cache::shouldReceive('remember')
    ->once()
    ->andReturn(collect());

15.2 Confirmar que las actualizaciones limpian la caché

Cache::expects('forget')
    ->with('home:popular-posts');

15.3 Confirmar que distintas condiciones generan distintas claves

Cuando cambian las condiciones de búsqueda o los tenant IDs, verifica que no se reutiliza la misma clave.

15.4 Confirmar el comportamiento de fallback ante fallos

Si una API externa falla, prueba que se use en su lugar el valor cacheado.

El objetivo del testing de caché no es demostrar velocidad, sino prevenir datos obsoletos y contaminación cruzada.


16. Monitoreo: observa no solo hit rates, sino también señales de incidentes

En la operación de caché, son útiles las siguientes métricas:

  • Hit rate
  • Miss rate
  • Uso de memoria de Redis
  • Crecimiento del número de claves
  • Número de adquisiciones fallidas de locks
  • Aumento repentino de llamadas a APIs externas

Además, desde la perspectiva del impacto en el usuario, conviene monitorear síntomas como:

  • Una página actualizada no refleja los cambios
  • Solo algunos tenants ven valores antiguos
  • La configuración antigua permanece después de un rollback
  • Los jobs batch se ejecutan dos veces

En otras palabras, además de métricas técnicas, es útil comprender los tipos de incidentes que la caché suele provocar desde un punto de vista operativo. Eso hace que la investigación sea mucho más rápida.


17. Errores comunes y cómo evitarlos

17.1 Cachear todo

Si cacheas cosas que cambian a menudo y son difíciles de invalidar, la gestión se vuelve rápidamente inmanejable.
Es más seguro empezar con listas pesadas, agregados y APIs externas.

17.2 Omitir condiciones en la clave

Si faltan locale, tenant, usuario o condiciones de búsqueda, pueden producirse colisiones de datos.
Aunque una clave se vuelva más larga, es más seguro si su significado está claro.

17.3 Intentar resolverlo todo solo con TTL

Para actualizaciones importantes, forget(), tags o claves versionadas suelen ser más fáciles de gestionar.

17.4 Escalar a múltiples servidores mientras sigues usando file cache

Esto hace más probables las inconsistencias de actualización y los problemas con locks.
Si se prevé operación multi-servidor, es necesario un diseño con almacenamiento compartido.

17.5 Olvidar config cache en producción, o usarlo siempre en desarrollo local

Mejora la velocidad en producción, pero en desarrollo local suele causar confusión porque los cambios no se reflejan.
Los distintos entornos deben operarse de forma distinta.


18. Checklist (para distribuir)

Diseño

  • [ ] Los objetivos de caché están categorizados en “visualización”, “agregados”, “locks” y “optimización de despliegue”
  • [ ] Los nombres de clave incluyen propósito, condiciones e información de dependencia
  • [ ] El TTL se determina según la frecuencia de actualización

Seguridad

  • [ ] Existe una política de forget() para las actualizaciones
  • [ ] Están identificados los lugares donde deben usarse locks para prevenir ejecución duplicada
  • [ ] Las cachés específicas por usuario y por tenant están separadas

Operación

  • [ ] Se usa un almacenamiento de caché compartido en entornos multi-servidor
  • [ ] Comandos como config:cache están incluidos en los despliegues de producción
  • [ ] Existe un procedimiento de emergencia con optimize:clear

HTTP / entrega

  • [ ] Se usa caché HTTP para páginas públicas y assets
  • [ ] Se considera ETag / Cache-Control según la frecuencia de actualización
  • [ ] Existe una estrategia para reflejar actualizaciones cuando se usa CDN

Accesibilidad

  • [ ] La carga se comunica con aria-busy o texto
  • [ ] Los resultados actualizados se comunican con role="status"
  • [ ] El estado no se comunica solo mediante color
  • [ ] El auto-refresh no es demasiado agresivo y también hay refresh manual

Testing

  • [ ] Existen pruebas para la invalidación de cachés importantes
  • [ ] Existen pruebas para claves basadas en condiciones
  • [ ] Se prueba el comportamiento de fallback ante fallos de APIs externas

19. Conclusión

La estrategia de caché en Laravel no consiste simplemente en añadir más llamadas a remember().
Solo se vuelve operativamente confiable cuando decides qué almacenar, en qué unidad, durante cuánto tiempo y cuándo limpiarlo.
Empieza por cosas pesadas y que toleren estar ligeramente desactualizadas, como listas, agregados y APIs externas. Añade estrategias de invalidación donde la frescura importe, usa locks donde la ejecución duplicada sea peligrosa y, en producción, convierte pasos de optimización como config:cache y route:cache en parte de tu proceso de despliegue. Del lado de la interfaz, comunica los estados de carga y actualización de forma accesible.
Una vez que este flujo está en su sitio, mejoras no solo la velocidad, sino también la claridad de la interfaz y la estabilidad ante incidentes. Un buen primer paso es tomar una pantalla de lista pesada y mejorarla con el conjunto de cuatro partes: diseño de clave, TTL, invalidación y comunicación de estado.


Enlaces de referencia

por greeden

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)