Saltar al contenido
DedicatedPHP Contactar

Invalidación de caché en PHP sin datos obsoletos

Diseña una caché PHP que acelere lecturas sin desactualizar permisos, disponibilidad, paneles ni configuraciones críticas.

Diagrama editorial de una aplicación PHP que invalida claves de caché tras actualizar datos en la base de datos

La invalidación de caché en PHP no consiste en elegir un TTL y almacenar respuestas en Redis. Es una decisión de consistencia: determinar qué información puede retrasarse, durante cuánto tiempo y qué debe ocurrir cuando cambia el dato original. Una caché mal diseñada puede mostrar un precio antiguo, conceder acceso con permisos revocados o presentar disponibilidad que ya no existe. Una caché demasiado conservadora, en cambio, traslada toda la carga a la base de datos y pierde su propósito.

El punto de partida es tratar cada entrada como un dato con propietario, ciclo de vida y riesgo definidos. Esto permite que producto, negocio y tecnología acuerden cuándo es aceptable una lectura potencialmente obsoleta y cuándo debe obtenerse el estado actual del origen de verdad.

Clasifique los datos antes de ponerlos en caché

Clasifique los datos antes de ponerlos en caché — guía visual de DedicatedPHP

No todos los datos frecuentes deben cachearse ni todos admiten el mismo mecanismo. Evalúe cada lectura con cuatro criterios: volatilidad, impacto de la desactualización, coste de consultar el origen y tolerancia a un fallo de caché.

  • Baja volatilidad y bajo impacto: catálogos públicos, metadatos o configuraciones no sensibles suelen admitir TTL de minutos u horas, según su proceso de cambio.
  • Volatilidad media: fichas de producto, agregados de paneles y resultados de búsqueda pueden cachearse si se invalidan al cambiar los registros que los componen.
  • Alto impacto: permisos, saldos, límites, estados transaccionales, inventario durante la confirmación de compra y controles de autorización requieren una fuente de verdad consistente o una estrategia explícita de frescura muy estricta.
  • Datos costosos de calcular: informes y resúmenes derivados pueden justificar caché aunque no se consulten mucho, pero deben declarar qué entidades los invalidan.

Es útil separar la caché de presentación de la caché de decisión. Mostrar durante unos segundos el nombre anterior de una categoría puede ser asumible. Usar una política antigua para autorizar una operación normalmente no lo es. Para decisiones críticas, consulte el origen autoritativo o almacene versiones que puedan verificarse antes de actuar.

Defina propiedad, claves y contratos de frescura

Cada entrada necesita una ficha operativa. Debe indicar el origen de verdad, la clave, los consumidores, el TTL máximo, el evento de invalidación, el comportamiento si Redis no está disponible y el responsable funcional o técnico. Sin este contrato, las claves se multiplican y nadie sabe qué borrar tras un cambio.

Use claves previsibles y con ámbito suficiente. Por ejemplo, product:42 representa una entidad concreta; tenant:8:product:42 evita mezclar datos entre organizaciones; y dashboard:tenant:8:period:current identifica un resultado derivado. No incluya datos secretos en claves ni use serializaciones inestables como identidad.

También conviene conservar un formato de valor uniforme: carga útil, versión o fecha de generación y, cuando sea relevante, un indicador de frescura. Un consumidor no debería asumir que una respuesta cacheada es equivalente a una lectura transaccionalmente consistente.

final class ProductCacheKey
{
    public static function detail(int $tenantId, int $productId): string
    {
        return "tenant:{$tenantId}:product:{$productId}:v1";
    }
}

El sufijo de esquema permite cambiar la estructura del valor sin tener que localizar y eliminar todas las entradas históricas. No sustituye la invalidación del dato de negocio, pero reduce riesgos durante una evolución de formato.

Elija el patrón de actualización según el tipo de lectura

Cache-aside para lecturas reutilizables

Con cache-aside, la aplicación busca primero la clave; ante ausencia, consulta la base de datos, construye el valor y lo guarda con TTL. Es simple y adecuado para lecturas relativamente estables. Su límite es claro: tras una escritura, alguien debe eliminar o reemplazar las entradas afectadas.

La invalidación debe producirse después de confirmar la transacción. Borrar antes del commit puede hacer que otro proceso reconstruya la caché con el valor aún antiguo. Si la aplicación publica eventos, un patrón outbox ayuda a registrar el cambio en la misma transacción y a entregar posteriormente la orden de invalidación de forma fiable.

Actualización explícita y versionado

Si una entidad se lee con mucha frecuencia y sus cambios son controlados, puede actualizarse la entrada tras confirmar la escritura. Así se evita el siguiente fallo de caché. Sin embargo, el proceso debe generar exactamente la misma representación que los lectores esperan; de lo contrario, invalidar y reconstruir suele ser menos arriesgado.

El versionado de claves es útil para dependencias amplias. En lugar de borrar todas las listas de productos de una organización, se incrementa tenant:8:products:version y las listas incorporan ese número en su clave. Las listas antiguas expiran solas. Este enfoque reduce borrados masivos, pero exige controlar el crecimiento de claves y no debe usarse para ocultar una dependencia mal entendida.

Controle carreras y dependencias derivadas

La carrera típica ocurre así: una lectura falla en caché, consulta el valor antiguo; una escritura confirma e invalida; la primera lectura termina y vuelve a guardar el valor antiguo. Para datos sensibles, combine invalidación con versión de entidad o bloqueo breve de reconstrucción. Antes de escribir el valor calculado, compruebe que la versión consultada sigue siendo la actual. Si no lo es, descarte el resultado y vuelva a leer.

Los bloqueos distribuidos deben ser cortos, tener expiración y no convertirse en un único punto de bloqueo. Su función es reducir reconstrucciones simultáneas, no asegurar por sí solos la consistencia de negocio. Si no se adquiere el bloqueo, una opción es esperar brevemente por el valor reconstruido o permitir una lectura directa limitada.

La invalidación por dependencia exige inventario. Un cambio de producto puede afectar a su detalle, a varias listas, a resultados de búsqueda, a contadores y a un panel. Un cambio de rol puede afectar a permisos efectivos de usuarios y a menús derivados. Modele estas relaciones de forma explícita:

  • Invalide la entidad directa mediante su clave.
  • Invalide o versione las colecciones y agregados que dependen de ella.
  • Recalcule de forma asíncrona los resultados costosos si la experiencia lo permite.
  • No confunda limpiar una vista con actualizar el origen de verdad.

Cuando la relación no es fácilmente enumerable, un espacio de versiones por organización, catálogo o política suele ser más seguro que intentar descubrir todas las claves afectadas mediante patrones de borrado global.

Use TTL, jitter y límites para proteger el origen

El TTL es una red de seguridad, no el único mecanismo de coherencia. Incluso una clave invalidada correctamente debe expirar: pueden existir fallos de entrega de eventos, errores de despliegue o entradas huérfanas. Elija TTL según el coste del error, no sólo según el coste de la consulta.

Aplique jitter aleatorio al TTL para que miles de claves creadas a la vez no expiren simultáneamente. Además, proteja el origen frente a una avalancha de fallos de caché mediante bloqueo de reconstrucción por clave, límites de concurrencia y cuotas por consumidor. Para datos no críticos, puede servirse un valor ligeramente vencido mientras un único proceso lo recalcula; para permisos o disponibilidad decisional, esa técnica debe descartarse o limitarse a escenarios expresamente aprobados.

Diseñe la degradación cuando Redis falla

Redis es una dependencia operativa, no el origen de verdad. Si no responde, la aplicación necesita un modo de degradación definido. Para una lectura pública poco costosa, puede consultar directamente la base de datos con límites de tiempo. Para consultas caras, conviene aplicar limitación de carga, reducir campos, responder con un estado temporalmente no disponible o utilizar una réplica apropiada si la arquitectura la contempla.

No convierta un fallo de caché en un agotamiento de conexiones de base de datos. Defina timeouts cortos, circuit breakers, presupuestos de consultas y métricas por ruta. En datos críticos, es preferible rechazar una operación que tomar una decisión con permisos, saldos o inventario cuya frescura no se puede garantizar.

Pruebe y observe la frescura, no sólo los aciertos

Una tasa de acierto alta no demuestra que la caché sea correcta. Instrumente aciertos, fallos, latencia, errores de lectura y escritura, TTL restante, bloqueos de reconstrucción, invalidaciones emitidas y fallidas, además de consultas y saturación de la base de datos. Relacione estas señales con cada familia de claves y no sólo con Redis como servicio global.

En pruebas, cubra al menos la lectura inicial, la actualización posterior, el borrado, la invalidación tras commit, la caída de caché y las carreras entre lector y escritor. Verifique que un usuario pierde acceso después de revocar un permiso, que una lista refleja una modificación según su contrato de frescura y que una invalidación fallida activa alertas o recuperación.

Lista de comprobación para una aplicación PHP existente

Lista de comprobación para una aplicación PHP existente — guía visual de DedicatedPHP
  1. Enumere las lecturas repetidas y clasifíquelas por riesgo, volatilidad y coste.
  2. Declare el origen de verdad y la tolerancia máxima de obsolescencia de cada dato.
  3. Documente claves, TTL, dependencias, consumidores y evento de invalidación.
  4. Ejecute invalidaciones o actualizaciones sólo tras el commit confirmado.
  5. Proteja reconstrucciones simultáneas y añada jitter a expiraciones relevantes.
  6. Defina el modo degradado ante indisponibilidad de Redis sin sobrecargar la base de datos.
  7. Mida frescura e invalidaciones, no únicamente el porcentaje de aciertos.
  8. Revise periódicamente claves sin propietario, TTL excesivos y dependencias no cubiertas.

Una estrategia fiable de invalidación de caché en PHP hace visibles sus compromisos: qué puede quedar obsoleto, por qué intervalo, cómo se corrige y qué ocurre cuando un componente falla. Esa claridad es más valiosa que añadir una caché de forma indiscriminada.

¿Quieres aplicar estas ideas a tu proyecto?Hablemos de tu plataforma PHP.
Ver servicio relacionado