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.
Una condición de memoria insuficiente (OOM) ocurre cuando un proceso intenta asignar memoria y falla porque el servidor no tiene memoria disponible. Esto suele dar como resultado el Linux OOM Killer, un proceso que el kernel de Linux utiliza cuando el sistema tiene 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 le da a la aplicación la posibilidad de cerrar MySQL sin problemas. Esto provoca los siguientes problemas:
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, se te ayudará a solucionar problemas de memoria de MySQL mediante el uso de performance_schema.
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 el registro y la supervisión. Cloud SQL reserva una parte de la memoria para el sistema operativo (SO) y los agentes de servicio, y asigna la memoria restante a los 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 del grupo de búferes de InnoDB (bytes) | Grupo de búferes (% de RAM de instancia) | Grupo de búferes (% de memoria 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% |
Tipo de máquina
GIB de RAM de la instancia (bytes)
Memoria mysqld (GB)
GiB del grupo de búferes de InnoDB (bytes)
Grupo de búferes (% de RAM de instancia)
Grupo de búferes (% de memoria 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%
Para las instancias que tienen 10 GB de RAM o más, el proceso mysqld está limitado a usar 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 supera el límite configurado del proceso mysqld, el cancelador OOM de Linux finaliza el proceso mysqld, ya que es el mayor consumidor de memoria de la instancia.
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 afecta la memoria. Consulta El modelo de asignación de memoria del esquema de rendimiento para comprender su asignación de memoria.
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 code_area es un valor como sql o Innodb y instrument_name 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, se recomienda esperar 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)
El esquema Sys contiene muchas vistas que resumen datos de performance_schema en formas fáciles de comprender. La mayoría de estas vistas vienen en pares. Comparten el mismo nombre y una de ellas 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> use sys;
Database changed
mysql> show tables like '%memory%';
+-------------------------------------+
| Tables_in_sys (%memory%) |
+-------------------------------------+
| memory_by_host_by_current_bytes |
| memory_by_thread_by_current_bytes |
| memory_by_user_by_current_bytes |
| memory_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)
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)
El objetivo de la vista sin el prefijo x$ es proporcionar resultados más fáciles de leer y usar. La vista con el prefijo x$ que muestra los mismos valores sin procesar es más para usarse con otras herramientas que realizan su propio procesamiento en los datos.
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 de forma descendente por la cantidad de memoria usada.
Antes de desglosar cada tipo de asignación, es bueno tener una descripción general del uso de memoria para cada área de código, como Innodb, SQL y performance_schema, entre otras.
La siguiente consulta agrega la memoria asignada actualmente por el área de código.
SELECT SUBSTRING_INDEX(event_name,'/',2) AS code_area,
FORMAT_BYTES(SUM(current_alloc)) AS current_alloc
FROM sys.x$memory_global_by_current_bytes
GROUP BY SUBSTRING_INDEX(event_name,'/',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)
En este resultado, se 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 directamente, se muestra el uso de memoria para las estructuras internas de MySQL, como se describe en la columna event_name. Si estás solucionando problemas relacionados con el aumento anormal de la memoria, puedes identificar la fuente observando el resultado de esta vista.
mysql> select event_name,current_alloc,high_alloc from 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 presentan los detalles de cada tipo de asignación.
Tipo de asignación | Detalles |
memory/innodb/buf_buf_pool | Memoria que usa el grupo de búferes de InnoDB |
memory/temptable/physical_ram | Memoria usada por tablas temporales internas |
memory/innodb/hash0hash | Memoria usada por la tabla de 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 mem |
Tipo de asignación
Detalles
memory/innodb/buf_buf_pool
Memoria que usa el grupo de búferes de InnoDB
memory/temptable/physical_ram
Memoria usada por tablas temporales internas
memory/innodb/hash0hash
Memoria usada por la tabla de 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 mem
En esta vista, se resume el uso de memoria agrupado por el usuario. Si un usuario tiene varias conexiones, se 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 ayudarte a identificar si una aplicación o un trabajo en particular consume mucha memoria.
En esta vista, se resume el uso de memoria agrupado por 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 del cliente. Es muy común tener varios clientes o servidores de aplicaciones en estos días. Esta vista te ayudará a identificar si algún cliente o carga de trabajo en particular está provocando un uso elevado de memoria. También puedes implementar el cambio primero en un cliente o servidor de aplicación, supervisar su impacto en la memoria (comparando el uso de memoria con el de otros hosts del cliente) y tomar decisiones fundamentadas.
En esta vista, se resume el uso de memoria, agrupado por subproceso, que es útil para 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 |
+-----------+-------------------+-------------------+
| 12999 | 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 lentos de consultas de MySQL o los registros generales con thread_id e identificar la sesión o las consultas que llevaron al crecimiento de la memoria.
Solucionar problemas con el uso elevado 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 del esquema sys 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:
Si te encuentras con una situación en la que una instancia de MySQL usa una cantidad de memoria más alta de la que debería, el uso de la memoria aumenta continuamente hasta que el servidor se queda sin memoria, y performance_schema muestra una brecha en la memoria total frente a la instrumentada o no indica a dónde se dirige la memoria, podría ser un síntoma de una fuga de memoria. Si sospechas que se produjo una fuga de memoria, puedes seguir estos pasos:
Comienza a desarrollar en Google Cloud con el crédito gratis de $300 y los más de 20 productos del nivel Siempre gratuito.