TPL_TILSOR_TEMPLATE_TILSOR TPL_TILSOR_TEMPLATE_TILSOR

Escalabilidad en MySQL: Thread Pool

Introducción

En este artículo presentamos una extensión de MySQL que nos permite manejar mayor cantidad de conexiones concurrentes.

Presentación del Problema

El servidor MySQL atiende las conexiones mediante el uso de threads, para cada conexión establecida se levanta un nuevo thread del lado del servidor.

A medida que la cantidad de conexiones aumenta, el uso de threads también. Luego de superar la cantidad de threads que el procesador puede manejar, la performance comienza a descender. La razón : todos aquellos threads que no pueden ser manejados deberán esperar en cola algún tiempo hasta poder ser atendidos.

Solución: thread pool

Qué es thread pool

Thread pool es plugin disponible en la versión Enterprise que nos permite manejar las conexiones de manera más eficiente.

La siguiente gráfica ilustra un benchmark realizado sobre dos bases de datos MySQL: una utilizando thread_pool (línea roja) y la otra no (línea azul)

Ilustración 1 - Comparación de rendimiento-(Ref: MySQL Enterprise Edition Product Guide Pag. 16)

En la prueba realizada podemos observar la respuesta del servidor (transacciones por segundo) a medida que se incrementa la carga (conexiones concurrentes). En el caso de una configuración sin thread_pool podemos observar que luego de alcanzar las 128 conexiones concurrentes respuesta del servidor empieza a degradarse, mientras que en la configuración que utiliza thread pool la respuesta se mantiene constante llegando, a atender 8192 conexiones concurrentes sin degradarse.

Cómo Funciona

Mediante el uso de thread pool las conexiones a la base de datos ya no se manejan levantando un thread por cada conexión, sino que son manejadas por un grupo de threads (thread group).

Ilustración 2- Manejo de las conexiones en thread pool – (Ref. MySQL Enterprise Edition Product Guide-A MySQL White Paper)

Cada conexión es asignada a un grupo de threads en forma rotativa (round robin). Los threads dentro de un grupo son reutilizados para atender sentencias de otras conexiones y se utilizan colas de prioridad.

En la siguiente sección explicamos en forma breve y simple el algoritmo.

Algoritmo

Cada grupo de threads tiene un thread destinado a recibir las conexiones entrantes denominado listener.

Cada vez que se recibe una nueva una nueva conexión ésta es manejada por el listener. Este procesa la sentencia entrante. Si la sentencia es “larga” de modo que mantenga a este thread ocupado por un cierto tiempo (por default 6ms) entonces se levanta un nuevo thread (listener) para atender nuevas conexiones. Si la transacción es corta (menor a 6 ms) entonces el thread ejecuta la misma y vuelve a quedar disponible. El umbral que define cuando una transacción se considera “corta” es configurable (ver sección Variables).

En el caso que la sentencia se vea bloqueada por un lock o por I/O, el grupo al que pertenece es notificado para que disponibilize un nuevo thread. De este modo otras conexiones no esperan por el thread bloqueado.

Colas de Prioridad

Existen dos colas de prioridad: prioridad alta y prioridad baja.

La primera sentencia de una transacción va a la cola de prioridad baja. Las siguientes sentencias de la misma transacción irán a la cola de prioridad alta mientras la transacción esté ejecutando sentencias. En caso contrario irán a la cola de baja prioridad.

El grupo seleccionará la siguiente sentencia a ejecutar comenzando por la cola de prioridad alta y luego sigue por las de la cola de prioridad baja.

Las sentencias que permanecen en la cola de prioridad baja por más de cierto tiempo son movidas a la cola de prioridad alta. El umbral de tiempo que define cuándo mover las sentencias a la cola de prioridad alta es configurable.

Variables

Las siguientes variables permiten configurar los umbrales descriptos en la sección anterior:

  • thread_pool_size: especifica la cantidad de thread groups
  • thread_pool_stall_limit: determina el umbral de tiempo a partir del que una sentencia se considera demorada (stalled), a partir del cual se disponibiliza un nuevo thread dentro del grupo
  • thread_pool_high_priority_connection: mediante esta variable podemos hacer que las sentencias encoladas para una misma sesión vayan a la cola de prioridad alta
  • thread_pool_prio_kickup_timer: Mediante esta variable podemos controlar el límite de tiempo antes que una sentencia sea movida de la cola de prioridad baja a la de prioridad alta

Conclusión

Thread pool reduce la cantidad de threads necesarios para manejar conexiones concurrentes y reduce la espera de las nuevas conexiones cuando los threads existentes están ocupados. El efecto neto es un manejo más eficiente de la concurrencia, logrando manejar un número mayor de conexiones con menor uso de recursos.

Referencias:

MySQL Enterprise Edition Product Guide-A MySQL White Paper - 2018,Oracle Corporation and/or its affiliates disponible en: https://www.mysql.com/why-mysql/white-papers/mysql-enterprise-edition-product-guide/

Mas infomación:

MySQL 8.0 Reference Manual- Sección 5.6.3 MySQL Enterprise Thread Pool - https://dev.mysql.com/doc/refman/8.0/en/thread-pool.html