Soluciona problemas de memoria de MySQL

Cloud SQL para MySQL de Google Cloud es un servicio de base de datos completamente administrado que te ayuda a configurar, mantener y administrar tus bases de datos de MySQL, PostgreSQL o SQL Server en Google Cloud Platform. Cloud SQL ejecuta una gran cantidad de bases de datos de MySQL en su flota, y es común ver que algunas instancias de MySQL consuman mucha memoria y fallen debido a un error de falta de memoria (OOM). En este artículo, encontrarás ayuda para solucionar problemas de memoria de MySQL.

Descripción general

Una condición de falta de memoria (OOM) ocurre cuando un proceso intenta asignar memoria y falla porque el servidor no tiene memoria disponible. Esto suele dar como resultado el OOM Killer de Linux, un proceso que emplea el kernel de Linux cuando el sistema tiene muy poca memoria, lo que finaliza el proceso de la base de datos. 

Una falla de la base de datos debido a una condición OOM no es ideal porque el proceso de la base de datos se finaliza con la directiva SIGKILL, lo que no permite que la aplicación se cierre con MySQL normal. Causa los siguientes problemas:

  • El servidor MySQL no funciona (mientras completa la recuperación ante fallas, que puede tardar varios minutos), lo que afecta a los usuarios y la empresa.
  • El servidor MySQL tarda más en preparar por completo los búferes y cachés para alcanzar el rendimiento máximo.
  • Posibilidad de daños en los datos debido a una falla inesperada

MySQL agregó instrumentación de memoria en performance_schema a partir de la versión 5.7, lo que ayuda a comprender la asignación de memoria de MySQL. En este artículo, encontrarás ayuda para solucionar problemas de memoria de MySQL mediante performance_schema.

Asignación de memoria de MySQL en Cloud SQL

Cada instancia de Cloud SQL funciona con una máquina virtual (VM) que se ejecuta en un servidor host de Google Cloud. Cada VM opera el programa de base de datos, como MySQL Server y los agentes de servicio que proporcionan servicios de asistencia, como registro y supervisión. Cloud SQL reserva una parte de la memoria para el sistema operativo (SO), los agentes de servicio y asigna la memoria restante a programas de bases de datos, como mysqld. 

En la siguiente tabla, se muestra la asignación de memoria para el proceso mysqld y la configuración predeterminada del grupo de búferes de InnoDB para cada tipo de máquina.

Tipo de máquina

GIB de RAM de la instancia (bytes)

Memoria mysqld (GB)

GiB de grupo de búferes de InnoDB (bytes)

Grupo de búferes (% de RAM de la instancia)

Grupo de búferes (% de la memoria de mysqld)

db-g1-small

1.7(1825361101)

1.11

0.625(671088640)

37%

56%

db-custom-1-3840

3.75(4026531840)

2.97

1.375(1476395008) 

37%

46%

db-custom-2-7680

7.5(8053063680)

6.72

4(4294967296)

53%

60%

db-custom-2-13312

13(13958643712)

12

9(9663676416)

69%

75%

db-custom-4-15360

15(16106127360)

14

10.5(11274289152) 

70%

76%

db-custom-4-26624

26(27917287424)

24

19(20401094656)

73%

79%

db-custom-8-30720

30(32212254720)

28

22(23622320128)

73%

80%

db-custom-8-53248

52(55834574848)

48

38(40802189312)

73%

79%

db-custom-16-61440

60(64424509440)

55

44(47244640256)

73%

80%

db-custom-16-106496

104(111669149696)

96

75(80530636800)

72%

78%

db-custom-32-122880

120(128849018880)

110

87(93415538688)

73%

79%

db-custom-32-212992

208(223338299392)

191

150(161061273600) 

72%

78%

db-custom-64-245760

240(257698037760)

221

173(185757335552)

72%

78%

db-custom-96-368640

360(386547056640)

331

260(279172874240)

72%

79%

db-custom-64-425984

416(446676598784)

383

300(322122547200)

72%

78%

db-custom-96-638976

624(670014898176)

574

450(483183820800) 

72%

78%

En el caso de las instancias que tienen 10 GB de RAM o más, el proceso mysqld está limitado a utilizar el 92% de la RAM de la instancia y el grupo de búferes de InnoDB se configura en el rango del 75 al 80% de la memoria disponible para el proceso mysqld. Además del grupo de búferes de InnoDB, MySQL asigna varios búferes y cachés para mejorar el rendimiento. Consulta este artículo para comprender la asignación de memoria en MySQL.

Cuando el uso de memoria de mysqld aumenta más allá del límite configurado del proceso mysqld, el eliminador de OOM de Linux finaliza el proceso mysqld, ya que es el mayor consumidor de memoria de la instancia.

performance_schema y Cloud SQL

performance_schema es una función de MySQL para monitorear la ejecución del servidor a bajo nivel. Cloud SQL para MySQL permite habilitar performance_schema en las instancias que tengan un tamaño de RAM de 3 GB o más. Está habilitado de forma predeterminada en instancias con al menos 15 GB de RAM, a partir de la versión MySQL 8.0.

Performance_schema está inhabilitado de forma predeterminada para las versiones de MySQL 5.6 y 5.7, y se puede habilitar con las marcas de base de datos.

La habilitación de performance_schema implica cierta sobrecarga de rendimiento y también implicaciones en la memoria. Consulta El modelo de asignación de memoria del esquema de rendimiento para comprender su asignación de memoria.

Instrumentación de memoria performance_schema

La instrumentación de memoria performance_schema ayuda a supervisar el uso de memoria en MySQL. Los instrumentos de memoria tienen nombres con la forma de memory/code_area/instrument_name , donde área_de_código es un valor como SQL o Innodb ynombre_instrumento es el detalle del instrumento.

MySQL 5.7 habilita algunos instrumentos de memoria de forma predeterminada, pero no todos. Para habilitarlos, actualiza la columna ENABLED de la tabla performance_schema.setup_instruments

mysql> select version();

+-------------------+

| version()         |

+-------------------+

| 5.7.39-google-log |

+-------------------+

1 row in set (0.00 sec)

mysql> SELECT enabled, count(*) FROM performance_schema.setup_instruments WHERE NAME LIKE '%memory%' group by enabled;

+---------+----------+

| enabled | count(*) |

+---------+----------+

| YES     |       70 |

| NO      |      306 |

+---------+----------+

2 rows in set (0.00 sec)

mysql> UPDATE performance_schema.setup_instruments SET ENABLED = 'YES' WHERE NAME LIKE 'memory/%';

Query OK, 306 rows affected (0.00 sec)

Rows matched: 376  Changed: 306  Warnings: 0

mysql> SELECT enabled, count(*) FROM performance_schema.setup_instruments WHERE NAME LIKE '%memory%' group by enabled;

+---------+----------+

| enabled | count(*) |

+---------+----------+

| YES     |      376 |

+---------+----------+

1 row in set (0.00 sec)

La habilitación de instrumentos de memoria en el tiempo de ejecución solo contará los objetos de memoria creados después de habilitar la instrumentación. Por lo tanto, te recomendamos que esperes a que el servidor recopile suficientes datos para solucionar problemas.

MySQL 8.0 habilita todos los instrumentos de memoria de forma predeterminada.

mysql> select version();

+---------------+

| version()     |

+---------------+

| 8.0.28-google |

+---------------+

1 row in set (0.00 sec)

mysql> SELECT enabled, count(*) FROM performance_schema.setup_instruments WHERE NAME LIKE '%memory%' group by enabled;

+---------+----------+

| enabled | count(*) |

+---------+----------+

| YES     |      490 |

+---------+----------+

1 row in set (0.00 sec)

Supervisa el uso de memoria con vistas de esquema del sistema

El esquema Sys contiene muchas vistas que resumen los datos de performance_schema en formatos fáciles de entender. La mayoría de estas vistas se dividen en parejas. Comparten el mismo nombre y uno de ellos tiene el prefijo x$. Las siguientes vistas están disponibles en MySQL 5.7 y 8.0 para ver la asignación de memoria en diferentes niveles.

mysql> usar sys;

Database changed

mysql> show tables like '%memory%';

+-------------------------------------+

| Tables_in_sys (%memory%)            |

+-------------------------------------+

| Memorystore_by_host_by_current_bytes     |

| Memorystore_by_thread_by_current_bytes   |

| Memorystore_by_user_by_current_bytes     |

| Memorystore_global_by_current_bytes      |

| memory_global_total                 |

| x$memory_by_host_by_current_bytes   |

| x$memory_by_thread_by_current_bytes |

| x$memory_by_user_by_current_bytes   |

| x$memory_global_by_current_bytes    |

| x$memory_global_total               |

+-------------------------------------+

10 rows in set (0.02 sec)

sys.memory_global_total

En la vista sys.memory_global_total, se resume el uso total de memoria en un formato legible.

mysql> select * from sys.memory_global_total;

+-----------------+

| total_allocated |

+-----------------+

| 12.43 GiB       |

+-----------------+

1 row in set (0.44 sec)

La vista sys.x$memory_global_total muestra la misma información, pero en bytes.

mysql> select * from sys.x$memory_global_total;

+-----------------+

| total_allocated |

+-----------------+

|     13351482992 |

+-----------------+

1 row in set (1.39 sec)

La vista sin el prefijo x$ pretende proporcionar un resultado más fácil de usar y de leer. La vista con el prefijo x$ que muestra los mismos valores sin procesar está diseñada más para usarse con otras herramientas que realizan su propio procesamiento en los datos.

sys.memory_global_by_current_bytes

La vista sys.memory_global_by_current_bytes muestra el uso de memoria actual agrupado por el tipo de asignación (event_name), por ejemplo, memory/innodb/buf_buf_pool (grupo de búferes de InnoDB). De forma predeterminada, las filas se ordenan por la cantidad de memoria usada en orden descendente. 

Antes de desglosar cada tipo de asignación, es conveniente tener una descripción general del uso de memoria para cada área de código, como Innodb, sql, performance_schema, entre otros. 

La siguiente consulta agrega la memoria asignada actualmente por el área de código. 

SELECT SUBSTRING_INDEX(event_name,'/',2) AS código_área,

FORMAT_BYTES(SUM(current_alloc)) AS current_alloc

DE sys.x$memory_global_by_current_bytes

GROUP BY SUBSTRING_INDEX(nombre_evento,'/',2)

ORDER BY SUM(current_alloc) DESC;

+---------------------------+---------------+

| code_area                 | current_alloc |

+---------------------------+---------------+

| memory/innodb             | 11.28 GiB     |

| memory/performance_schema | 520.02 MiB    |

| memory/temptable          | 514.00 MiB    |

| memory/sql                | 106.12 MiB    |

| memory/mysys              | 43.25 MiB     |

| memory/vio                | 372.15 KiB    |

| memory/myisam             |  696 bytes    |

| memory/csv                |   88 bytes    |

| memory/blackhole          |   88 bytes    |

+---------------------------+---------------+

9 rows in set (1.02 sec)

Este resultado muestra que innodb es el mayor consumidor de memoria, seguido de performance_schema y las tablas temporales.

Ahora, cuando se consulta sys.memory_global_by_current_bytes, se muestra directamente el uso de memoria de las estructuras internas de MySQL, como se describe en la columna event_name. Si quieres solucionar el problema del aumento anormal de memoria, puedes identificar la fuente observando el resultado de esta vista.

mysql> select event_name,current_alloc,high_alloc desde sys.memory_global_by_current_bytes limit 5;

+-------------------------------+---------------+------------+

| event_name                    | current_alloc | high_alloc |

+-------------------------------+---------------+------------+

| memory/innodb/buf_buf_pool    | 10.72 GiB     | 10.72 GiB  |

| memory/temptable/physical_ram | 514.00 MiB    | 515.00 MiB |

| memory/innodb/hash0hash       | 292.69 MiB    | 292.69 MiB |

| memory/innodb/memory          | 191.77 MiB    | 197.94 MiB |

| memory/sql/TABLE              | 60.39 MiB     | 62.35 MiB  |

+-------------------------------+---------------+------------+

5 rows in set (0.45 sec)

A continuación, se muestran los detalles de cada tipo de asignación.

Tipo de asignación

Detalles

memory/innodb/buf_buf_pool

Memoria usada por el grupo de búferes de InnoDB

memory/temptable/physical_ram

Memoria usada por las tablas temporales internas

memory/innodb/hash0hash

Memoria usada por la tabla hash de InnoDB

memory/innodb/memory

Memoria usada por varios búferes de InnoDB

memory/sql/TABLE

Memoria usada por los objetos TABLE y su raíz de memoria

sys.memory_by_user_by_current_bytes

Esta vista resume el uso de memoria agrupado por el usuario. Si un usuario tiene múltiples conexiones, suma el uso de memoria de todas las instancias del usuario.

mysql> select user,current_allocated from memory_by_user_by_current_bytes;

+-----------------+-------------------+

| user            | current_allocated |

+-----------------+-------------------+

| sbtest          | 60.30 MiB         |

| background      | 2.68 MiB          |

| root            | 1.52 MiB          |

| event_scheduler | 16,38 KiB         |

+-----------------+-------------------+

4 rows in set (1.16 sec)

Si usas un usuario diferente para cada aplicación o trabajo (recomendado), esta vista puede ayudar a identificar si una aplicación o un trabajo en particular consume mucha memoria.

sys.memory_by_host_by_current_bytes

En esta vista, se resume el uso de memoria agrupado por el nombre de host.

mysql> select host,current_allocated from memory_by_host_by_current_bytes;

+------------+-------------------+

| host       | current_allocated |

+------------+-------------------+

| 10.128.0.8 | 62.10 MiB         |

| background | 2.70 MiB          |

| 127.0.0.1  | 311.01 KiB        |

| localhost  | 16,38 KiB         |

+------------+-------------------+

4 rows in set (1.58 sec)

Esta vista es útil para identificar el uso de memoria por host de cliente. En estos días, tener varios servidores de clientes o aplicaciones es muy común. Esta vista ayudará a identificar si algún cliente o carga de trabajo en particular está causando un alto uso de la memoria. También puedes implementar el cambio en un servidor de cliente o aplicación primero, supervisar su impacto en la memoria (comparando el uso de memoria con el de otros hosts cliente) y tomar decisiones fundamentadas.

sys.memory_by_thread_by_current_bytes

Esta vista resume el uso de memoria, agrupado por subproceso, y ayuda a encontrar los subprocesos que ocupan la mayor parte de la memoria.

mysql> select thread_id,user,current_allocated from sys.memory_by_thread_by_current_bytes limit 5;

+-----------+-------------------+-------------------+

| thread_id | user              | current_allocated |

+-----------+-------------------+-------------------+

|     12,999 | sbtest@10.128.0.8 | 3.80 MiB          |

|     12888 | sbtest@10.128.0.8 | 3.75 MiB          |

|     12760 | sbtest@10.128.0.8 | 3.68 MiB          |

|     13128 | sbtest@10.128.0.8 | 3.67 MiB          |

|     13221 | sbtest@10.128.0.8 | 3.47 MiB          |

+-----------+-------------------+-------------------+

5 rows in set (2.29 sec)

Si descubres que un subproceso en particular está consumiendo mucha memoria, puedes filtrar los registros de consultas lentos de MySQL o los registros generales con el subproceso_id y, luego, identificar la sesión o las consultas que generaron el crecimiento de la memoria.

Conclusión

La solución de problemas de alto uso de memoria es una tarea difícil. Afortunadamente, la instrumentación de memoria performance_schema ayuda a los usuarios a comprender cómo las estructuras internas de MySQL usan la memoria. Las vistas de esquema del sistema facilitan la interpretación de la información. Si tienes problemas relacionados con la memoria, esta función te ayudará a encontrar el origen del problema y podrás solucionarlo realizando cambios en la configuración o la consulta. 

Por ejemplo:

  1. Si las tablas temporales internas requieren una gran cantidad de memoria, puedes limitar el tamaño de las tablas temporales mediante tmp_table_size o reducir innodb_buffer_pool_size para hacer espacio para los búferes por sesión.
  2. Si un subproceso en particular consume mucha memoria, puedes filtrar los registros de consultas lentos mediante thread_id para identificar las consultas problemáticas y ajustarlas a fin de reducir el uso de memoria para ese subproceso o sesión.

Si te encuentras con una situación en la que una instancia de MySQL usa una memoria más alta de lo que debería y el uso aumenta continuamente hasta que el servidor se queda sin memoria y el performance_schema muestra un espacio entre la memoria total y la instrumentada, o no te dice dónde podría ser un síntoma de una fuga de memoria. Si sospechas que hay una fuga de memoria, puedes seguir estos pasos:

  • Revisa detenidamente la configuración de MySQL y confirma que no haya sobreasignaciones de búferes ni cachés. Este artículo te ayudará a comprender cómo MySQL asigna memoria para diferentes búferes y cachés.
  • Identifica la función, configuración o consulta que causa el problema de memoria; intenta reproducir el problema.
  • Revisa las notas de la versión de MySQL de las siguientes versiones secundarias de la misma serie para verificar si se corrigieron algún error con comportamiento similar. 
  • Actualiza MySQL a la versión secundaria más reciente para ver si eso soluciona el problema de memoria.
  • Busca en la base de datos de errores de MySQL para comprobar si otros usuarios de la comunidad informaron algún problema similar.
  • Crea un error nuevo con un caso de prueba reproducible si no existe un error para el mismo problema.

Da el siguiente paso

Comienza a desarrollar en Google Cloud con el crédito gratis de $300 y los más de 20 productos del nivel Siempre gratuito.

Google Cloud
  • ‪English‬
  • ‪Deutsch‬
  • ‪Español‬
  • ‪Español (Latinoamérica)‬
  • ‪Français‬
  • ‪Indonesia‬
  • ‪Italiano‬
  • ‪Português (Brasil)‬
  • ‪简体中文‬
  • ‪繁體中文‬
  • ‪日本語‬
  • ‪한국어‬
Consola
Google Cloud