La sesión php gc puede bloquear solicitudes porque, al limpiar decenas de miles de archivos de sesión, ocupa el proceso PHP durante mucho tiempo y, por lo tanto, otras solicitudes quedan en espera. Muestro cómo la limpieza probabilística, los bloqueos de archivos y la lentitud de la E/S provocan retrasos notables y cómo evito estos retrasos con ajustes claros, tareas cron y almacenamiento RAM, para que la sitio web permanece líquido.
Puntos centrales
- causa del problema: La GC probabilística, la E/S de archivos y los bloqueos provocan tiempos de espera.
- factor de riesgo: Muchas sesiones (por ejemplo, 170 000) prolongan cada ejecución de GC.
- WordPress: El administrador + Heartbeat agravan los retrasos.
- Alojamiento: La RAM, el SSD y el aislamiento reducen los costes de GC.
- Solución: La limpieza de Cron y Redis aceleran las solicitudes.
Breve explicación de la recolección de basura de sesiones PHP
Las sesiones almacenan datos de estado entre solicitudes, normalmente como archivos en el sistema de archivos. La recolección de basura elimina los archivos obsoletos cuyo tiempo de modificación es anterior a session.gc_maxlifetime, a menudo 1440 segundos. De forma predeterminada, PHP inicia esta limpieza de forma probabilística mediante session.gc_probability y session.gc_divisor, a menudo como 1 de cada 1000 llamadas. Suena inofensivo, pero con un tráfico intenso, siempre hay alguien que tiene que soportar toda la ejecución. Cuantos más archivos haya en el directorio de sesión, más tiempo bloqueará la Limpieza el proceso.
¿Por qué bloquea la limpieza las solicitudes?
Una ejecución de GC debe enumerar el directorio de sesión, comprobar cada archivo y eliminar las entradas antiguas, lo que en E/S lentas puede Segundos cuesta. Si hay 170 000 archivos, se producen muchas llamadas al sistema consecutivas que consumen CPU, RAM y almacenamiento. Los procesos PHP iniciados en paralelo a veces intentan eliminar al mismo tiempo y provocan bloqueos de archivos adicionales. Esto aumenta los tiempos de espera, ya que los procesos se ralentizan o bloquean entre sí. Si se profundiza en Bloqueo de sesión , se da cuenta de lo mucho que el bloqueo afecta al perfil de tiempo de respuesta y aumenta el tiempo hasta el primer byte, especialmente en los picos de carga, que quiero evitar utilizando la GC desacoplar.
WordPress: páginas de administración lentas debido a las sesiones
El área de administración requiere más CPU y accesos a la base de datos que el frontend, lo que hace que cualquier retraso adicional sea notable. hace. Si la recolección de basura se inicia justo en ese momento, el tiempo hasta la salida HTML final aumenta considerablemente. La API Heartbeat también consulta al servidor y, con mala suerte, colisiona con una ejecución de GC. Esto hace que el backend parezca lento y que los clics tarden más, aunque la lógica real no haga gran cosa. Lo mitigo estableciendo la probabilidad de GC en las solicitudes en cero y la trabajo de limpieza planificable fuera de los tiempos de respuesta.
Servicios de alojamiento e infraestructura
En sistemas compartidos, muchos proyectos comparten la capacidad de E/S, lo que hace que una sola ejecución de GC afecte a otros sitios web. frenos. Un hardware mejor con almacenamiento NVMe rápido y suficiente RAM reduce los costes por acceso a los archivos. Un aislamiento limpio por cliente o contenedor evita que los picos de carga externos afecten a tu proyecto. También compruebo los límites de los procesos y los programadores de E/S para que muchos trabajadores PHP simultáneos no se atasquen. Si quieres planificar más a fondo, encontrarás en una Optimización del alojamiento web Puntos de partida concretos para desacoplar las ejecuciones GC y la Latencia estabilizar.
Sesiones en el sistema de archivos frente a almacenes RAM
Las sesiones basadas en archivos son sencillas, pero generan mucho Sobrecarga al buscar, comprobar y eliminar. Los almacenes basados en RAM, como Redis o Memcached, gestionan las claves de forma eficiente, ofrecen un rendimiento rápido y cuentan con mecanismos de caducidad integrados. Esto ahorra llamadas al sistema, reduce la latencia y disminuye la propensión a errores debido a bloqueos de archivos. Prefiero el almacenamiento en RAM cuando aumenta el número de visitantes o el área de administración responde con lentitud. La conversión se realiza rápidamente y hay una guía para Gestión de sesiones con Redis ayuda a que la configuración sea clara y la Recursos aprovechar mejor.
Configuración adecuada de PHP para sesiones
Configuraré la recolección de basura para que ninguna solicitud la active accidentalmente. desencadena. Para ello, establezco la probabilidad en cero, planifico la limpieza mediante Cron y ajusto la vida útil en función del riesgo. Además, activo modos estrictos para que PHP solo acepte ID válidos. Compruebo la memoria y la ruta para que no haya montajes NFS lentos o directorios saturados que ralenticen el proceso. La siguiente tabla muestra los valores predeterminados habituales y los valores probados que selecciono según el caso de uso para optimizar el Actuación mejorar de forma cuantificable.
| Configuración | Estándar típico | Recomendación | Efecto |
|---|---|---|---|
| session.gc_maxlifetime | 1440 segundos | 900-3600 segundos | Una vida útil más corta reduce los archivos antiguos y disminuye E/S. |
| session.gc_probability / session.gc_divisor | 1 / 1000 (frecuente) | 0 / 1 | Sin limpieza en las solicitudes, Cron se encarga limpieza. |
| session.save_handler | archivos | redis o memcached | El almacenamiento RAM reduce los bloqueos de archivos y acorta Latencias. |
| session.use_strict_mode | 0 | 1 | Solo identificaciones válidas, menos colisiones y Riesgos. |
| session.save_path | ruta del sistema | Ruta rápida propia | Poca profundidad del directorio, SSD local, menos Estadística-Visitas. |
Además, observo otros interruptores que mejoran la estabilidad y la seguridad sin generar sobrecarga:
- session.use_only_cookies=1, session.use_cookies=1 para un uso claro de cookies sin ID de URL.
- session.cookie_httponly=1, session.cookie_secure=1 (en HTTPS) y un session.cookie_samesite adecuado (normalmente Lax) para evitar fugas.
- session.lazy_write=1 para ahorrar accesos de escritura innecesarios cuando el contenido no cambia.
- session.serialize_handler=php_serialize para una serialización moderna e interoperabilidad.
- Aumentar session.sid_length y session.sid_bits_per_character para hacer que los ID sean más robustos.
Configuración concreta: php.ini, .user.ini y FPM
Anclo los ajustes donde funcionan de forma fiable: globalmente en php.ini, por grupo en PHP‑FPM o localmente mediante .user.ini en proyectos que tienen necesidades específicas. Un conjunto pragmático tiene el siguiente aspecto:
; php.ini o FPM-Pool session.gc_probability = 0 session.gc_divisor = 1 session.gc_maxlifetime = 1800 session.use_strict_mode = 1 session.use_only_cookies = 1 session.cookie_httponly = 1
session.cookie_secure = 1 session.cookie_samesite = Lax session.lazy_write = 1 ; más rápido, ruta local o almacenamiento en RAM ; session.save_handler = files ; session.save_path = "2;/var/lib/php/sessions"
En el grupo FPM puedo establecer valores de forma rígida para que las aplicaciones individuales no los sobrescriban:
; /etc/php/*/fpm/pool.d/www.conf php_admin_value[session.gc_probability] = 0
php_admin_value[session.gc_divisor] = 1 php_admin_value[session.gc_maxlifetime] = 1800 php_admin_value[session.save_path] = "2;/var/lib/php/sessions"
Sistema de archivos y diseño de la ruta de guardado
Los directorios grandes con cientos de miles de archivos son lentos. Por eso, comparto el directorio de sesión en subcarpetas para que las búsquedas en el directorio sean rápidas:
session.save_path = "2;/var/lib/php/sessions" El 2 inicial genera dos subcarpetas de nivel, basadas en partes hash del ID de sesión. Además, ayudan opciones de montaje como noatime, así como un sistema de archivos con buenos índices de directorio. Evito NFS para sesiones, si es posible, o fuerzo sesiones persistentes en el equilibrador de carga hasta que un almacén RAM sea productivo.
Desactivar el bloqueo en el código
Muchos retrasos no solo se deben al GC, sino también a bloqueos innecesariamente prolongados. Abro la sesión lo más brevemente posible:
<?php session_start(); // leer $data = $_SESSION['key'] ?? null; session_write_close(); // liberar bloqueo temprano // trabajo costoso sin bloqueo $result = heavy_operation($data);
// volver a abrir y escribir solo si es necesario session_start(); $_SESSION['result'] = $result; session_write_close();
Si solo leo, inicio la sesión con read_and_close para que PHP ni siquiera entre en modo de escritura:
true]); // solo lectura, no es necesario escribir
De este modo, se reduce la probabilidad de que las solicitudes paralelas tengan que esperar unas a otras. En los plugins de WordPress, compruebo si session_start() es realmente necesario y traslado la llamada a hooks posteriores para que no se bloquee el flujo principal.
Configuración de la memoria RAM para sesiones
En Redis o Memcached, presto atención a los tiempos de espera, las bases de datos y la política de almacenamiento. Un ejemplo sólido de Redis es el siguiente:
session.save_handler = redis session.save_path = "tcp://127.0.0.1:6379?database=2&timeout=2&read_timeout=2&persistent=1" session.gc_maxlifetime = 1800 session.gc_probability = 0 session.gc_divisor = 1
Como los almacenes RAM gestionan ellos mismos los tiempos de expiración, me ahorro el GC de archivos. Ejecuto las sesiones separadas de las cachés (otra base de datos o prefijo de clave) para que las expulsiones de claves de caché no descarten sesiones de forma involuntaria. La política de almacenamiento la baso en volatile-LRU, de modo que solo se desplazan las claves con TTL cuando la memoria se agota.
Limpieza externa mediante Cron: así es como equilibro las solicitudes
La forma más segura de lograr el desacoplamiento es colocando el GC fuera del flujo de solicitudes. iniciar. Establezco la probabilidad en PHP‑ini o mediante .user.ini en 0 y ejecuto regularmente un pequeño script mediante Cron que inicia la limpieza. Lo ideal es que Cron se ejecute cada minuto o cada cinco minutos, dependiendo del tráfico y de la higiene deseada. Es importante que el cron funcione con el mismo usuario que el servidor web para que los permisos sean correctos. Además, compruebo los registros y las métricas para asegurarme de que la limpieza programada Rutina funciona de manera fiable.
Para las sesiones basadas en archivos utilizo dos variantes probadas:
- Una línea de PHP que llama al GC interno (a partir de PHP 7.1):
*/5 * * * * php -d session.gc_probability=1 -d session.gc_divisor=1 -r 'session_gc();' 2>/dev/null
- Una limpieza de búsqueda que compara el mtime con la vida útil deseada:
*/5 * * * * find /var/lib/php/sessions -type f -mmin +30 -delete
Estoy atento al tiempo de ejecución de Cron. Si cinco minutos no son suficientes, aumento la frecuencia o reduzco el tiempo de vida. En configuraciones muy frecuentadas, Cron se ejecuta cada minuto para mantener el directorio pequeño.
Diagnóstico y supervisión
Reconozco los picos de GC por el aumento de los tiempos de respuesta y los picos de E/S llamativos en el Monitoreo. Las herramientas en el contexto de WordPress, como Query Monitor, ayudan a identificar hooks, plugins y llamadas de administración que funcionan con lentitud. Una mirada a los registros de acceso y de errores muestra cuándo las solicitudes tardan mucho más tiempo. Muchos picos pequeños de 200 ms son normales, pero los picos de varios segundos indican bloqueos o GC. Si además se observa el número de archivos y el tamaño del directorio, se puede ver cómo se llena el directorio de sesión y por qué un limpieza es necesario.
Herramientas prácticas para la búsqueda de causas:
- Activar php-fpm slowlog y request_slowlog_timeout para ver los puntos de bloqueo.
- iotop, iostat, pidstat y vmstat para detectar la presión de E/S y los cambios de contexto.
- strace -p a corto plazo para observar archivos abiertos y bloqueos.
- find | wc -l en la ruta de la sesión para medir la cantidad de archivos.
- Latencias TTFB y p95/p99 en APM para cuantificar las mejoras tras la migración.
Comprobaciones específicas de WordPress
Compruebo los plugins que llaman a session_start() temprano y sustituyo los candidatos con uso innecesario de la sesión por Alternativas. En el administrador, reduzco la frecuencia de latido o la limito a las páginas del editor. Las cachés no deben eludir las sesiones, ya que de lo contrario el efecto se esfuma; por eso controlo cuidadosamente las excepciones. También es importante no crear sesiones para invitados si no hay motivo para ello. De este modo, la cantidad de archivos por día se reduce notablemente y el GC tiene menos trabajo.
En entornos WooCommerce, presto especial atención a las funciones del carrito de la compra y los fragmentos que generan sesiones para usuarios anónimos. A menudo basta con iniciar las sesiones solo cuando se producen interacciones reales (inicio de sesión, pago). Además, me aseguro de que WP-Cron no genere mucha carga en paralelo: dejo que WP-Cron sea iniciado por un cron del sistema y desactivo la ejecución por solicitud. Esto evita que las tareas cron colisionen con las operaciones de sesión.
Seguridad, vida útil y experiencia del usuario
Las vidas más largas mantienen a los usuarios conectados, pero aumentan la cantidad de datos antiguos. Sesiones. Los valores más cortos reducen la carga, pero pueden finalizar las sesiones antes. Por lo tanto, elijo períodos que equilibran el riesgo y la comodidad, por ejemplo, 30-60 minutos en el administrador y menos para los usuarios anónimos. Para contenidos especialmente sensibles, establezco modos estrictos y cookies seguras contra XSS y errores de transporte. De esta manera, los datos permanecen protegidos, mientras que la Actuación sigue siendo fiable.
Después de iniciar sesión, roto los ID de sesión (session_regenerate_id(true)) para evitar la fijación y utilizo cookie_same_site, httponly y secure de forma sistemática. En escenarios de inicio de sesión único, planifico deliberadamente la elección de SameSite (Lax vs. None) para que la experiencia del usuario se mantenga estable.
Clústeres, equilibradores de carga y sesiones persistentes
Si se utilizan varios servidores de aplicaciones, las sesiones basadas en archivos solo deben utilizarse con sesiones persistentes, ya que, de lo contrario, los usuarios perderán su estado. Es mejor utilizar un almacén RAM centralizado. Compruebo la latencia entre la aplicación y el almacén, configuro tiempos de espera cortos, pero no agresivos, y planifico una conmutación por error (por ejemplo, Sentinel/Cluster en Redis). Durante el mantenimiento, es importante seleccionar los TTL de manera que una breve interrupción no provoque inmediatamente un cierre de sesión masivo.
Consideraciones económicas y trayectoria migratoria
Cambiar a Redis o Memcached tiene un coste operativo, pero ahorra dinero. Tiempo por solicitud y reduce los casos de asistencia técnica. Quienes trabajan a menudo en la administración notan la diferencia de inmediato. Valoro el ahorro que supone una implementación más rápida, menos frustraciones y menos interrupciones. Cuando aumenta la carga, planifico la migración con antelación para evitar cuellos de botella. Una hoja de ruta clara incluye pruebas, implementación y supervisión hasta que la Latencia estables y las carreras de GC sigan siendo discretas.
Para la transición, procedo paso a paso: en Staging, activo el RAM Store, ejecuto una carga sintética y compruebo las latencias p95/p99. A continuación, lo implemento en pequeños porcentajes mediante un indicador de función y observo las tasas de error y los tiempos de espera. La reversión es sencilla si puedo variar session.name en paralelo, de modo que las sesiones entre el backend antiguo y el nuevo no entren en conflicto. Las cifras clave son: archivos de sesión por hora (debe disminuir), TTFB mediano (debe disminuir), tasa 5xx (debe permanecer estable) y porcentaje de solicitudes con bloqueo de sesión superior a 100 ms (debe disminuir considerablemente).
Brevemente resumido
La sesión php gc provoca retrasos porque las operaciones de limpieza iniciadas aleatoriamente provocan largas operaciones de archivo y bloqueos. disparar. Lo soluciono estableciendo la probabilidad en cero en las solicitudes, planificando la limpieza mediante Cron y almacenando las sesiones en la memoria RAM. Los recursos de alojamiento con NVMe rápido y suficiente RAM reducen aún más el bloqueo. WordPress se beneficia notablemente cuando se controla Heartbeat, se revisan los plugins y se evitan las sesiones innecesarias. Si se siguen estos pasos, se reducen los tiempos de respuesta, se evitan los bloqueos y se mantiene la Admin-Interfaz reactiva, incluso con mucho tráfico.


