...

Configurar de forma eficiente la caché de subprocesos de MariaDB: mayor rendimiento con menos sobrecarga

Configuró específicamente la caché de subprocesos de MariaDB para aliviar la carga del establecimiento de conexiones y la creación de subprocesos. De este modo, reduce Latencia y ahorra CPU‑Sobrecarga, sobre todo cuando hay muchas sesiones cortas y una alta frecuencia de conexión.

Puntos centrales

Los siguientes aspectos constituyen las pautas para una configuración y medición eficaces de la caché. Me centraré en aspectos claros Valores y aplicables Pasos.

  • Mecanismo de acción: Reutilización de subprocesos finalizados en lugar de crearlos de nuevo, lo cual resulta más costoso
  • Relevancia: Útil cuando hay muchas conexiones breves por segundo
  • Medición: Threads_created, Connections, Threads_cached
  • Límites: Se ignora si el grupo de subprocesos está activo
  • Procedimiento: Empezar poco a poco, evaluar y aumentar gradualmente

Así funciona la caché de subprocesos de MariaDB

Tras cerrar una conexión, MariaDB almacena el hilo en una caché, siempre que no se haya alcanzado el límite. Las nuevas conexiones pueden reutilizar este hilo, lo que evita la costosa creación del mismo y reduce el Tiempo de respuesta disminuye. Esto tiene un efecto especial cuando hay muchos inicios de sesión por segundo y cargas de trabajo con sesiones cortas, en las que la creación y destrucción de subprocesos supone un Factor coste . La caché se vacía tras unos cinco minutos de inactividad, lo que evita que el servidor acumule cargas innecesarias. Sin un grupo de subprocesos, el valor por defecto suele ser 256, lo que ofrece un pequeño margen para los picos habituales. Además, quiero señalar que la reutilización no resuelve todos los problemas: las conexiones deficientes o las estrategias de cliente erróneas siguen siendo evidentes y requieren correcciones específicas.

Cuándo merece la pena el tuning

Aumento el tamaño de la caché cuando la aplicación genera muchas conexiones breves y el contador Hilos_creados crece a un ritmo vertiginoso. Una señal clara es una ratio elevada de Threads_created dividido entre Connections, ya que en ese caso la reutilización no cumple su objetivo con demasiada frecuencia. En este caso, los nuevos hilos reducen la CPU y ralentizan el tiempo de respuesta, mientras que Reuse acorta la ruta. Sin embargo, siempre compruebo si la causa no está en el cliente, por ejemplo, debido a reconexiones innecesarias. Si una gestión adecuada de las conexiones alivia la carga, a menudo basta con un ajuste moderado de la caché. Quien maximiza a ciegas, pronto lo paga con un mayor consumo de memoria y pasa por alto los verdaderos puntos de ajuste en la lógica de la aplicación.

Valores de medición que compruebo previamente

Para establecer un diagnóstico preciso, utilizo unos pocos indicadores, pero muy significativos, con fórmulas claras. Para empezar, leo Hilos_creados, Conexiones, Hilos_almacenados_en_la_memoria_caché y Hilos_conectados y compruebo las tendencias. La sencilla ratio Threads_created/Connections me indica con qué frecuencia la base de datos se recrea en lugar de reutilizarse. También resulta muy útil que el valor de Threads_cached se mantenga cercano al número habitual de conexiones simultáneas en horas punta. Si la caché y la diferencia respecto al pico siguen siendo elevadas, regalo Recursos o reúnete con la Carga No. La siguiente tabla recoge los principales indicadores y su significado directo:

Cifra clave Significado interpretación Acción
Hilos_creados Hilos creados desde el inicio Un crecimiento rápido indica una regeneración frecuente Comprobar la caché, reducir las reconexiones de los clientes
Conexiones Número total de conexiones Base para el cálculo de la cuota y la evaluación de la tendencia Seguir la evolución de los picos de carga
Hilos_almacenados_en_la_memoria_caché Hilos en la caché Aunque la frecuencia sea alta, un valor bajo puede ser demasiado pequeño Aumentar la caché poco a poco
Hilos_conectados Conexiones activas actualmente Pauta para determinar un tamaño adecuado de la caché Dimensionar la caché en función de los picos típicos

Adaptación gradual en la práctica

Empiezo realizando mediciones bajo una carga realista y registro los parámetros clave antes de cada cambio. A continuación, compruebo el valor actual con Mostrar variables como 'thread_cache_size' y anota el Base para más adelante Comparaciones. A continuación, voy aumentando poco a poco y observo si «Threads_created» aumenta más lentamente y si los tiempos de conexión se estabilizan. Un único cambio importante puede ocultar las causas, por lo que apuesto deliberadamente por pasos pequeños y verificables. Tras cada ajuste, espero a que se produzca una fase de carga significativa para que el efecto sea fiable. Solo cuando varias ventanas de carga confirman el panorama, me planteo el siguiente paso.

Lógica de configuración recomendada y valores iniciales

No existe un valor ideal universal, por lo que me baso en los picos habituales y en el historial. Para velocidades de conexión bajas o medias, suele bastar con una caché pequeña o mediana, especialmente si se acerca al estándar de 256. Cuando la carga varía mucho y hay muchas conexiones por segundo, un tamaño mayor resulta útil, siempre y cuando la reutilización aumente realmente. Mantengo la caché ligeramente por debajo de los picos habituales de Threads_connected, para no generar Recursos . Quien amplía demasiado la caché, desperdicia memoria sin obtener ninguna ventaja. Además, tengo en cuenta los procesos en segundo plano asociados, como el Hilos de Page Cleaner, ya que también influyen en el comportamiento general cuando hay una elevada actividad de E/S.

Necesidades de memoria por hilo y repercusiones del tamaño de la caché

Tengo en cuenta deliberadamente el efecto de almacenamiento de la caché. Un hilo almacenado en la caché conserva principalmente su thread_stack y metadatos de hilo reducidos. Búferes por conexión como sort_buffer_size, join_buffer_size o el búfer de red se liberan al desconectarse y no sobrecargan la caché de forma permanente. La pila, en cambio, permanece vinculada al hilo. Como regla general, parto de la base de que: Memoria caché ≈ thread_cache_size × thread_stack (más un pequeño margen). En el caso de un thread_stack Con 256-320 KB y una caché de 512, esto ya supone un orden de magnitud de 130-170 MB de memoria ocupada. Quien aumente la pila o utilice cachés muy grandes debería tener en cuenta este efecto y sopesarlo frente a los búferes más importantes (por ejemplo, los búferes de InnoDB).

Por eso siempre compruebo lo siguiente:

  • SHOW VARIABLES LIKE 'thread_stack'; para conocer la memoria asignada a cada hilo
  • La proximidad de Hilos_almacenados_en_la_memoria_caché en el punto máximo del percentil 95 de Hilos_conectados
  • Si el aumento del tamaño de la caché influye en la cuota Hilos creados / Conexiones realmente mejorado

Si no se obtienen beneficios, volveré a reducirlo. Un caché demasiado grande se nota porque Hilos_almacenados_en_la_memoria_caché se mantiene constantemente por encima del pico habitual de conexión, sin que las latencias sigan disminuyendo.

Funcionamiento en Linux y en contenedores: límites y obstáculos

Compruebo los límites del sistema antes de aumentar el tamaño de la caché. La creación de subprocesos puede fallar debido a las restricciones del sistema operativo mucho antes de que la propia base de datos alcance el límite máximo de conexiones. Para ello, compruebo lo siguiente:

  • Límites de procesos y subprocesos: ulimit -u (máx. procesos/hilos), /proc/sys/kernel/threads-max y /proc/sys/kernel/pid_max
  • Límite de pila: ulimit -s influye en la pila reservada por hilo; en conjunto, es relevante para cachés de gran tamaño
  • cgroups en el contenedor: pids.max y los límites de memoria; unos límites de PID demasiado estrictos frenan las ráfagas
  • Impresión del programador: Cuando hay un gran número de subprocesos sin grupo, la sobrecarga por cambio de contexto puede aumentar; en estos casos, puede resultar más adecuado utilizar un grupo de subprocesos o un grupo de aplicaciones

En hosts con múltiples sockets o NUMA, compruebo además si los hilos saltan de un nodo a otro y provocan, con ello, accesos remotos a la memoria. En este tipo de entornos, los pools estables suelen ser más eficientes que la creación constante de nuevos hilos, que el programador distribuye de forma dispersa.

Equívocos habituales en torno a la caché de subprocesos

Despejo los errores más comunes para optimizar de forma específica:

  • „Más caché = cada vez más rápido“.“ Solo si realmente se crean muchos hilos nuevos, la caché sale ganando. De lo contrario, estoy ocupando memoria sin ningún beneficio.
  • „La caché agiliza el proceso de autenticación.“ La caché ahorra principalmente la creación de subprocesos del sistema operativo. La autenticación, el protocolo de enlace TLS y, en su caso, las consultas DNS se realizan por cada conexión y siguen siendo aspectos que requieren optimización por separado.
  • „Los búferes por hilo siguen ocupados“.“ Tras la desconexión, estos búferes se liberan; en la caché permanece principalmente la pila del hilo.
  • „Una caché de gran tamaño sustituye al agrupamiento de aplicaciones“.“ La caché del servidor reduce los costes, pero el agrupamiento de aplicaciones los evita. Siempre considero el agrupamiento de aplicaciones como la primera opción a tener en cuenta.

Influencia de TLS, DNS y la autenticación

Evalúo los tiempos de conexión de forma diferenciada, ya que la caché no abarca todas las partes. Altos Tiempos de Handshake A menudo lo interpreto como un problema relacionado con TLS (verificación del certificado, falta de reanudación) o con la resolución inversa de DNS. Con skip_name_resolve=ON Evito las costosas búsquedas inversas y me baso en autorizaciones basadas en IP. La elección y la configuración del complemento de autenticación también influyen en la ruta de inicio de sesión. Por el contrario, la caché de subprocesos reduce principalmente el coste de Creación y destrucción de subprocesos. Si, a pesar de tener una caché grande, sigo observando latencias de conexión elevadas, me centro en los parámetros TLS, el DNS y la gestión de conexiones del cliente.

Lógica de decisión: ¿caché, grupo de subprocesos o agrupación de aplicaciones?

Tomo la decisión siguiendo un camino sencillo:

  • ¿Está disponible la función de agrupación de aplicaciones? En ese caso, dimensiona correctamente. Se hunde Hilos_creados Está claro que basta con una caché pequeña o mediana como reserva.
  • ¿Está activo el grupo de subprocesos? Entonces entra en juego tamaño_cache_hilos No. Ajusto el pool y mido los tiempos de espera antes de modificar otros parámetros.
  • ¿Muchas relaciones cortas sin compartir pareja? Aumentar moderadamente la caché. Objetivo: reducir notablemente la tasa. Hilos creados / Conexiones y horarios de conexión más tranquilos.
  • ¿Un nivel muy alto de paralelismo y una gran carga sobre el programador? Estoy estudiando la posibilidad de pasar al pool de subprocesos, que puede ofrecer «work-stealing» y cuotas de trabajadores más ajustadas.

Lo importante es tener un plan de contingencia: si un enfoque no funciona de forma apreciable mejor, revierto el último cambio. De este modo, me mantengo fiel a los datos y evito una complejidad que no aporta ningún valor añadido.

Metodología de medición con ejemplos de consultas

Utilizo consultas reproducibles para demostrar los avances. A modo de instantánea:

  • SHOW GLOBAL STATUS LIKE 'Threads\_%'; suministros Hilos_creados, Hilos_almacenados_en_la_memoria_caché, Hilos_conectados
  • SHOW GLOBAL STATUS LIKE 'Connections'; para la base de cálculo de la cuota
  • SHOW VARIABLES LIKE 'thread\_%'; en tamaño_cache_hilos y thread_stack comprobar

Calculo la cuota, por ejemplo, de la siguiente manera:

SELECT 
  ROUND(tc.variable_value+0 / NULLIF(c.variable_value+0, 0), 4) AS threads_created_per_connection
FROM information_schema.GLOBAL_STATUS tc
JOIN information_schema.GLOBAL_STATUS c
  ON tc.variable_name='Threads_created' AND c.variable_name='Connections';

Para las pruebas de carga, utilizo intervalos de tiempo. Tomo dos instantáneas (al inicio y al final de un intervalo de entre 5 y 10 minutos) y calculo las diferencias. Opcionalmente, utilizo un entorno de pruebas aislado ESTADO DE FLUSH, para restablecer los contadores; en producción evito hacerlo para no interferir en otros análisis. Además de la tasa, guardo los percentiles 95 y 99 de la duración de la conexión procedentes de la monitorización del cliente, ya que es precisamente ahí donde se aprecian los efectos en los picos de latencia.

Detectar: la caché es demasiado pequeña

A menudo detecto que la caché es demasiado pequeña porque el valor de «Threads_created» aumenta considerablemente con una carga constante. Al mismo tiempo, el valor de «Threads_cached» se mantiene bajo, aunque el sistema procese muchas conexiones y la tasa de rendimiento sea baja. El resultado son fluctuaciones en Latencias y innecesarias CPU‑Carga debida a la creación frecuente de hilos. Si la caché aumenta de forma moderada y los indicadores se estabilizan, esto confirma el diagnóstico. Si los tiempos se estabilizan y la tasa mejora notablemente, significa que voy por el buen camino. Si no se produce el efecto deseado, busco de forma específica causas relacionadas con los clientes, problemas de red o cuellos de botella en el almacenamiento.

Detectar: la caché es demasiado grande

Una caché demasiado grande suele pasar más desapercibida, pero puede ocupar memoria de la que carecen otros objetivos de almacenamiento temporal. En ese caso, obtengo una tasa que ya es buena, pero aumentar la caché apenas cambia nada y solo sobrecarga la Recursos. Si el valor de Threads_cached se mantiene de forma permanente muy por encima del pico habitual, la ventaja se esfuma. Lo reduzco gradualmente y compruebo si cambian los indicadores o los tiempos de respuesta. Si todo se mantiene estable, me decanto por la configuración más pequeña y eficiente. De este modo, mantengo la instancia optimizada y dejo espacio para áreas de memoria más importantes, como el búfer de InnoDB y las estructuras sustitutivas de la caché de consultas.

Característica especial: grupo de subprocesos activo

En cuanto se pone en marcha el grupo de subprocesos, MariaDB ignora por completo la variable `thread_cache_size`. En este modo, un grupo controla unos pocos trabajadores que atienden numerosas conexiones, lo que evita los tiempos de espera para los nuevos subprocesos. En función del perfil de carga, decido si conviene utilizar el grupo de derecha ¿El enfoque es ese o la caché es mayor? Flexibilidad proporciona. Las cargas de trabajo altamente paralelizadas suelen beneficiarse del pool, mientras que los picos clásicos de inicio de sesión funcionan bien con la reutilización de la caché. Quien utilice el pool debe centrarse en sus parámetros y dejar de lado el parámetro `thread_cache_size`. Un buen punto de partida es leer sobre el Pool de subprocesos de MariaDB, antes de planificar nuevas medidas de puesta a punto.

Interacción con el grupo de conexiones de la aplicación

Prefiero un pool integrado en la aplicación, porque mantiene las conexiones abiertas y alivia la carga del servidor de la base de datos. Si el valor de `Threads_created` se mantiene bajo a pesar de una carga elevada, esto indica que el pool funciona eficazmente y que hay poca necesidad de caché adicional. En esta configuración, suele bastar con una caché pequeña que amortigüe picos aislados y no Recursos desperdiciado. Si, por el contrario, observo reconexiones constantes, lo primero que hay que revisar es que el agrupamiento de aplicaciones funcione correctamente y, solo después, aumentar la configuración de la base de datos. Analizar los tiempos de inactividad y los tamaños de los grupos ayuda a encontrar el punto óptimo para una carga uniforme. Una guía práctica ofrece información útil para empezar sobre Agrupación de conexiones, que utilizo en paralelo a la optimización de la caché.

Ejemplo: Configuración y control

En primer lugar, compruebo la configuración actual con Mostrar variables como 'thread_cache_size' y documento la carga. A continuación, introduzco, a modo de prueba, un valor moderado como SET GLOBAL thread_cache_size = 256; o 512, dependiendo de las puntas. Es importante realizar un cambio permanente en el archivo de configuración, por ejemplo, en my.cnf en [mysqld], para que al reiniciar se mantenga la configuración. En las siguientes ventanas de carga observo Hilos_creados y el Cita, hasta que detecte una tendencia clara. Si la generación de nuevos datos disminuye notablemente, la caché cumple su objetivo. Si las cifras no varían, busco las causas en la gestión de la conexión antes de seguir aumentando el valor.

Guía práctica: dimensionamiento con barreras de seguridad

Trabajo con valores de referencia fiables, en lugar de buscar la maximización a ciegas:

  • Inicio: Máximos actuales de Hilos_conectados observar (a lo largo de varias ventanas de carga típicas).
  • Primero, el dimensionamiento: Caché ≈ 70-90 % del pico habitual, además de estar limitado por un techo como max_connections / 2 como límite de seguridad.
  • Incremento: Aumentar en pequeños incrementos de 64 a 128 y la proporción Hilos creados / Conexiones Compruébalo.
  • Rango objetivo: Descenso significativo de la tasa y percentiles 95 más estables en los tiempos de conexión; si el efecto no se produce, eliminar la caché.
  • Persistencia: A partir de las versiones de MariaDB con SET PERSIST Guardo los valores comprobados directamente en el servidor; de lo contrario, en my.cnf.
  • Rollback: Antes de cada cambio, anoto el valor anterior para poder revertirlo rápidamente en caso de duda.

En entornos con perfiles diurnos y nocturnos muy dispares, recomiendo un dimensionamiento conservador que suavice los picos sin ocupar una cantidad innecesaria de memoria durante la noche. Para cargas especiales (implementaciones, oleadas de Cron), preveo deliberadamente un margen de seguridad.

Lista de comprobación para la resolución de problemas

Primero compruebo si el grupo de subprocesos está activo y, por lo tanto, desactiva la caché. A continuación, mido la relación entre Threads_created y Connections en varias ventanas de tiempo, en lugar de limitarme a tomar una instantánea. A continuación, comparo Threads_cached con el pico de Threads_connected para detectar un sobredimensionamiento o un subdimensionamiento. Si el rendimiento sigue siendo bajo, analizo las reconexiones de la aplicación, las latencias de red y las señales de almacenamiento, como el aumento de los tiempos de espera de E/S. Por último, compruebo los ajustes concurrentes que influyen en los hilos y elaboro escenarios de prueba repetibles. Solo así puedo extraer conclusiones claras y evitar tomar medidas precipitadas sin una base de datos sólida.

Versión abreviada para los que tienen prisa

Utilizo la caché de subprocesos para reutilizar los subprocesos y reducir los costes de creación. Esto surte efecto cuando la frecuencia de conexiones es elevada, mientras que un grupo de subprocesos activo ignora esta variable. El éxito se mide a través de una disminución de la tasa de Hilos_creados a Conexiones y tiempos de conexión más estables. Empiezo poco a poco, realizo mediciones de forma sistemática y solo aumento los valores cuando las cifras y el perfil lo justifican. El agrupamiento por parte del cliente suele ser la herramienta más eficaz, por lo que lo compruebo primero ahí. Así consigo un mayor rendimiento con menos sobrecarga y mantengo una configuración sencilla.

Artículos de actualidad