Solución de problemas de uso de memoria de bases de datos Aurora MySQL - Amazon Aurora

Solución de problemas de uso de memoria de bases de datos Aurora MySQL

Si bien CloudWatch, Supervisión mejorada e Información de rendimiento proporcionan una visión general óptima del uso de la memoria en el sistema operativo, por ejemplo, la cantidad de memoria que utiliza el proceso de la base de datos, no permiten desglosar qué conexiones o componentes del motor podrían estar causando este uso de memoria.

Para solucionar este problema, puede utilizar Performance Schema y el esquema de sys. En Aurora MySQL versión  3, la instrumentación de memoria se habilita de forma predeterminada cuando se habilita Performance Schema. En Aurora MySQL versión  2, solo se habilita por defecto la instrumentación de memoria para el uso de memoria de Performance Schema. Para obtener información sobre las tablas disponibles en Performance Schema para realizar un seguimiento del uso de la memoria y habilitar la instrumentación de memoria de Performance Schema, consulte la tablas de resumen de memoria en la documentación de MySQL. Para obtener más información sobre el uso de Performance Schema con Información de rendimiento, consulte Descripción general de Performance Schema para Información de rendimiento en Aurora MySQL.

Si bien se encuentra disponible información detallada en Performance Schema para realizar un seguimiento del uso actual de la memoria, el esquema sys de MySQL tiene vistas en la parte superior de las tablas de Performance Schema que puede utilizar para identificar rápidamente dónde se utiliza la memoria.

En el esquema sys, están disponibles las siguientes vistas para realizar un seguimiento del uso de la memoria por conexión, componente y consulta.

Visualización Descripción

memory_by_host_by_current_bytes

Proporciona información sobre el uso de la memoria del motor por el host. Esto puede resultar útil para identificar qué servidores de aplicaciones o hosts de clientes consumen memoria.

memory_by_thread_by_current_bytes

Proporciona información sobre el uso de la memoria del motor por ID de subproceso. El ID de subproceso en MySQL puede ser una conexión de cliente o un subproceso en segundo plano. Puede asignar ID de subprocesos a ID de conexión de MySQL mediante la vista sys.processlist o la tabla performance_schema.threads.

memory_by_user_by_current_bytes

Proporciona información sobre el uso de la memoria del motor por usuario. Esto puede resultar útil para identificar qué cuentas de usuario o clientes consumen memoria.

memory_global_by_current_bytes

Proporciona información sobre el uso de la memoria del motor por componente del motor. Esto puede resultar útil para identificar el uso de memoria global por parte de búferes o componentes del motor. Por ejemplo, es posible que vea el evento memory/innodb/buf_buf_pool del conjunto de búferes de InnoDB o el evento memory/sql/Prepared_statement::main_mem_root de las instrucciones preparadas.

memory_global_total

Proporciona una descripción general del uso total de memoria del que se ha hecho un seguimiento en el motor de base de datos.

En Aurora MySQL versión  3.05 y versiones posteriores, también puede realizar un seguimiento del uso de memoria máximo mediante resumen de instrucciones en las tablas de resumen de instrucciones de Performance Schema. Las tablas de resumen de instrucciones contienen resúmenes normalizados de instrucciones y estadísticas agregadas sobre su ejecución. La columna MAX_TOTAL_MEMORY puede ayudarle a identificar la memoria máxima utilizada por el resumen de consultas desde la última vez que se restablecieron las estadísticas o desde que se reinició la instancia de la base de datos. Esto puede resultar útil para identificar consultas específicas que podrían estar consumiendo mucha memoria.

nota

Performance Schema y el esquema sys muestran el uso actual de memoria en el servidor y los niveles máximos de memoria consumida por conexión y componente del motor. Como Performance Schema se mantiene en la memoria, la información se restablece cuando se reinicia la instancia de base de datos. Para mantener un historial a lo largo del tiempo, le recomendamos que configure la recuperación y el almacenamiento de estos datos fuera de Performance Schema.

Ejemplo 1: uso elevado y continuo de memoria

Si observamos globalmente FreeableMemory en CloudWatch, podemos ver que el uso de memoria ha aumentado en gran medida a las 02:59 UTC del 26 de marzo de 2024.

Gráfico de FreeableMemory que muestra un uso elevado de memoria.

Esto no nos muestra el panorama completo. Para determinar qué componente está consumiendo más memoria, puede iniciar sesión en la base de datos y ver sys.memory_global_by_current_bytes. Esta tabla contiene una lista de eventos de memoria de los que hace seguimiento MySQL, junto con información sobre la asignación de memoria por evento. Cada evento de seguimiento de memoria comienza con memory/%, seguido de otra información sobre el componente o la característica del motor al que está asociado el evento.

Por ejemplo, memory/performance_schema/% es para eventos de memoria relacionados con Performance Schema, memory/innodb/% es para InnoDB, etc. Para obtener información sobre las convenciones de nomenclatura de los eventos, consulte Performance Schema Instrument Naming Conventions en la documentación de MySQL.

A partir de la siguiente consulta, podemos encontrar al probable culpable en función de current_alloc, pero también podemos ver muchos eventos de memory/performance_schema/%.

mysql> SELECT * FROM sys.memory_global_by_current_bytes LIMIT 10; +-----------------------------------------------------------------------------+---------------+---------------+-------------------+------------+------------+----------------+ | event_name | current_count | current_alloc | current_avg_alloc | high_count | high_alloc | high_avg_alloc | +-----------------------------------------------------------------------------+---------------+---------------+-------------------+------------+------------+----------------+ | memory/sql/Prepared_statement::main_mem_root | 512817 | 4.91 GiB | 10.04 KiB | 512823 | 4.91 GiB | 10.04 KiB | | memory/performance_schema/prepared_statements_instances | 252 | 488.25 MiB | 1.94 MiB | 252 | 488.25 MiB | 1.94 MiB | | memory/innodb/hash0hash | 4 | 79.07 MiB | 19.77 MiB | 4 | 79.07 MiB | 19.77 MiB | | memory/performance_schema/events_errors_summary_by_thread_by_error | 1028 | 52.27 MiB | 52.06 KiB | 1028 | 52.27 MiB | 52.06 KiB | | memory/performance_schema/events_statements_summary_by_thread_by_event_name | 4 | 47.25 MiB | 11.81 MiB | 4 | 47.25 MiB | 11.81 MiB | | memory/performance_schema/events_statements_summary_by_digest | 1 | 40.28 MiB | 40.28 MiB | 1 | 40.28 MiB | 40.28 MiB | | memory/performance_schema/memory_summary_by_thread_by_event_name | 4 | 31.64 MiB | 7.91 MiB | 4 | 31.64 MiB | 7.91 MiB | | memory/innodb/memory | 15227 | 27.44 MiB | 1.85 KiB | 20619 | 33.33 MiB | 1.66 KiB | | memory/sql/String::value | 74411 | 21.85 MiB | 307 bytes | 76867 | 25.54 MiB | 348 bytes | | memory/sql/TABLE | 8381 | 21.03 MiB | 2.57 KiB | 8381 | 21.03 MiB | 2.57 KiB | +-----------------------------------------------------------------------------+---------------+---------------+-------------------+------------+------------+----------------+ 10 rows in set (0.02 sec)

Mencionamos anteriormente que Performance Schema se almacena en la memoria, lo que significa que también se realiza un seguimiento de ello en la instrumentación de memoria de performance_schema.

nota

Si observa que Performance Schema utiliza mucha memoria y quiere limitar su uso, puede ajustar los parámetros de la base de datos en función de sus necesidades. Para obtener más información, consulte The Performance Schema memory-allocation model en la documentación de MySQL.

Para facilitar la lectura, puede volver a ejecutar la misma consulta pero excluir los eventos de Performance Schema. En el resultado se observa lo siguiente:

  • El elemento que consume más memoria es memory/sql/Prepared_statement::main_mem_root.

  • La columna current_alloc nos indica que MySQL tiene 4,91 GiB actualmente asignados a este evento.

  • high_alloc column nos indica que 4,91 GiB es el nivel máximo de current_alloc desde que se restablecieron las estadísticas por última vez o desde que se reinició el servidor. Esto significa que memory/sql/Prepared_statement::main_mem_root está en su valor más alto.

mysql> SELECT * FROM sys.memory_global_by_current_bytes WHERE event_name NOT LIKE 'memory/performance_schema/%' LIMIT 10; +-----------------------------------------------+---------------+---------------+-------------------+------------+------------+----------------+ | event_name | current_count | current_alloc | current_avg_alloc | high_count | high_alloc | high_avg_alloc | +-----------------------------------------------+---------------+---------------+-------------------+------------+------------+----------------+ | memory/sql/Prepared_statement::main_mem_root | 512817 | 4.91 GiB | 10.04 KiB | 512823 | 4.91 GiB | 10.04 KiB | | memory/innodb/hash0hash | 4 | 79.07 MiB | 19.77 MiB | 4 | 79.07 MiB | 19.77 MiB | | memory/innodb/memory | 17096 | 31.68 MiB | 1.90 KiB | 22498 | 37.60 MiB | 1.71 KiB | | memory/sql/String::value | 122277 | 27.94 MiB | 239 bytes | 124699 | 29.47 MiB | 247 bytes | | memory/sql/TABLE | 9927 | 24.67 MiB | 2.55 KiB | 9929 | 24.68 MiB | 2.55 KiB | | memory/innodb/lock0lock | 8888 | 19.71 MiB | 2.27 KiB | 8888 | 19.71 MiB | 2.27 KiB | | memory/sql/Prepared_statement::infrastructure | 257623 | 16.24 MiB | 66 bytes | 257631 | 16.24 MiB | 66 bytes | | memory/mysys/KEY_CACHE | 3 | 16.00 MiB | 5.33 MiB | 3 | 16.00 MiB | 5.33 MiB | | memory/innodb/sync0arr | 3 | 7.03 MiB | 2.34 MiB | 3 | 7.03 MiB | 2.34 MiB | | memory/sql/THD::main_mem_root | 815 | 6.56 MiB | 8.24 KiB | 849 | 7.19 MiB | 8.67 KiB | +-----------------------------------------------+---------------+---------------+-------------------+------------+------------+----------------+ 10 rows in set (0.06 sec)

Por el nombre del evento, podemos decir que esta memoria se está utilizando para instrucciones preparadas. Si quiere ver qué conexiones utilizan esta memoria, puede consultar memory_by_thread_by_current_bytes.

En el siguiente ejemplo, cada conexión tiene aproximadamente 7 MiB asignados, con un nivel máximo de aproximadamente 6,29 MiB (current_max_alloc). Esto tiene sentido, ya que el ejemplo utiliza sysbench con 80 tablas y 800 conexiones con instrucciones preparadas. Si desea reducir el uso de memoria en este caso, puede optimizar el uso que hace su aplicación de las instrucciones preparadas para reducir el consumo de memoria.

mysql> SELECT * FROM sys.memory_by_thread_by_current_bytes; +-----------+-------------------------------------------+--------------------+-------------------+-------------------+-------------------+-----------------+ | thread_id | user | current_count_used | current_allocated | current_avg_alloc | current_max_alloc | total_allocated | +-----------+-------------------------------------------+--------------------+-------------------+-------------------+-------------------+-----------------+ | 46 | rdsadmin@localhost | 405 | 8.47 MiB | 21.42 KiB | 8.00 MiB | 155.86 MiB | | 61 | reinvent@10.0.4.4 | 1749 | 6.72 MiB | 3.93 KiB | 6.29 MiB | 14.24 MiB | | 101 | reinvent@10.0.4.4 | 1845 | 6.71 MiB | 3.72 KiB | 6.29 MiB | 14.50 MiB | | 55 | reinvent@10.0.4.4 | 1674 | 6.68 MiB | 4.09 KiB | 6.29 MiB | 14.13 MiB | | 57 | reinvent@10.0.4.4 | 1416 | 6.66 MiB | 4.82 KiB | 6.29 MiB | 13.52 MiB | | 112 | reinvent@10.0.4.4 | 1759 | 6.66 MiB | 3.88 KiB | 6.29 MiB | 14.17 MiB | | 66 | reinvent@10.0.4.4 | 1428 | 6.64 MiB | 4.76 KiB | 6.29 MiB | 13.47 MiB | | 75 | reinvent@10.0.4.4 | 1389 | 6.62 MiB | 4.88 KiB | 6.29 MiB | 13.40 MiB | | 116 | reinvent@10.0.4.4 | 1333 | 6.61 MiB | 5.08 KiB | 6.29 MiB | 13.21 MiB | | 90 | reinvent@10.0.4.4 | 1448 | 6.59 MiB | 4.66 KiB | 6.29 MiB | 13.58 MiB | | 98 | reinvent@10.0.4.4 | 1440 | 6.57 MiB | 4.67 KiB | 6.29 MiB | 13.52 MiB | | 94 | reinvent@10.0.4.4 | 1433 | 6.57 MiB | 4.69 KiB | 6.29 MiB | 13.49 MiB | | 62 | reinvent@10.0.4.4 | 1323 | 6.55 MiB | 5.07 KiB | 6.29 MiB | 13.48 MiB | | 87 | reinvent@10.0.4.4 | 1323 | 6.55 MiB | 5.07 KiB | 6.29 MiB | 13.25 MiB | | 99 | reinvent@10.0.4.4 | 1346 | 6.54 MiB | 4.98 KiB | 6.29 MiB | 13.24 MiB | | 105 | reinvent@10.0.4.4 | 1347 | 6.54 MiB | 4.97 KiB | 6.29 MiB | 13.34 MiB | | 73 | reinvent@10.0.4.4 | 1335 | 6.54 MiB | 5.02 KiB | 6.29 MiB | 13.23 MiB | | 54 | reinvent@10.0.4.4 | 1510 | 6.53 MiB | 4.43 KiB | 6.29 MiB | 13.49 MiB | . . . . . . | 812 | reinvent@10.0.4.4 | 1259 | 6.38 MiB | 5.19 KiB | 6.29 MiB | 13.05 MiB | | 214 | reinvent@10.0.4.4 | 1279 | 6.38 MiB | 5.10 KiB | 6.29 MiB | 12.90 MiB | | 325 | reinvent@10.0.4.4 | 1254 | 6.38 MiB | 5.21 KiB | 6.29 MiB | 12.99 MiB | | 705 | reinvent@10.0.4.4 | 1273 | 6.37 MiB | 5.13 KiB | 6.29 MiB | 13.03 MiB | | 530 | reinvent@10.0.4.4 | 1268 | 6.37 MiB | 5.15 KiB | 6.29 MiB | 12.92 MiB | | 307 | reinvent@10.0.4.4 | 1263 | 6.37 MiB | 5.17 KiB | 6.29 MiB | 12.87 MiB | | 738 | reinvent@10.0.4.4 | 1260 | 6.37 MiB | 5.18 KiB | 6.29 MiB | 13.00 MiB | | 819 | reinvent@10.0.4.4 | 1252 | 6.37 MiB | 5.21 KiB | 6.29 MiB | 13.01 MiB | | 31 | innodb/srv_purge_thread | 17810 | 3.14 MiB | 184 bytes | 2.40 MiB | 205.69 MiB | | 38 | rdsadmin@localhost | 599 | 1.76 MiB | 3.01 KiB | 1.00 MiB | 25.58 MiB | | 1 | sql/main | 3756 | 1.32 MiB | 367 bytes | 355.78 KiB | 6.19 MiB | | 854 | rdsadmin@localhost | 46 | 1.08 MiB | 23.98 KiB | 1.00 MiB | 5.10 MiB | | 30 | innodb/clone_gtid_thread | 1596 | 573.14 KiB | 367 bytes | 254.91 KiB | 970.69 KiB | | 40 | rdsadmin@localhost | 235 | 245.19 KiB | 1.04 KiB | 128.88 KiB | 808.64 KiB | | 853 | rdsadmin@localhost | 96 | 94.63 KiB | 1009 bytes | 29.73 KiB | 422.45 KiB | | 36 | rdsadmin@localhost | 33 | 36.29 KiB | 1.10 KiB | 16.08 KiB | 74.15 MiB | | 33 | sql/event_scheduler | 3 | 16.27 KiB | 5.42 KiB | 16.04 KiB | 16.27 KiB | | 35 | sql/compress_gtid_table | 8 | 14.20 KiB | 1.77 KiB | 8.05 KiB | 18.62 KiB | | 25 | innodb/fts_optimize_thread | 12 | 1.86 KiB | 158 bytes | 648 bytes | 1.98 KiB | | 23 | innodb/srv_master_thread | 11 | 1.23 KiB | 114 bytes | 361 bytes | 24.40 KiB | | 24 | innodb/dict_stats_thread | 11 | 1.23 KiB | 114 bytes | 361 bytes | 1.35 KiB | | 5 | innodb/io_read_thread | 1 | 144 bytes | 144 bytes | 144 bytes | 144 bytes | | 6 | innodb/io_read_thread | 1 | 144 bytes | 144 bytes | 144 bytes | 144 bytes | | 2 | sql/aws_oscar_log_level_monitor | 0 | 0 bytes | 0 bytes | 0 bytes | 0 bytes | | 4 | innodb/io_ibuf_thread | 0 | 0 bytes | 0 bytes | 0 bytes | 0 bytes | | 7 | innodb/io_write_thread | 0 | 0 bytes | 0 bytes | 0 bytes | 0 bytes | | 8 | innodb/io_write_thread | 0 | 0 bytes | 0 bytes | 0 bytes | 0 bytes | | 9 | innodb/io_write_thread | 0 | 0 bytes | 0 bytes | 0 bytes | 0 bytes | | 10 | innodb/io_write_thread | 0 | 0 bytes | 0 bytes | 0 bytes | 0 bytes | | 11 | innodb/srv_lra_thread | 0 | 0 bytes | 0 bytes | 0 bytes | 0 bytes | | 12 | innodb/srv_akp_thread | 0 | 0 bytes | 0 bytes | 0 bytes | 0 bytes | | 18 | innodb/srv_lock_timeout_thread | 0 | 0 bytes | 0 bytes | 0 bytes | 248 bytes | | 19 | innodb/srv_error_monitor_thread | 0 | 0 bytes | 0 bytes | 0 bytes | 0 bytes | | 20 | innodb/srv_monitor_thread | 0 | 0 bytes | 0 bytes | 0 bytes | 0 bytes | | 21 | innodb/buf_resize_thread | 0 | 0 bytes | 0 bytes | 0 bytes | 0 bytes | | 22 | innodb/btr_search_sys_toggle_thread | 0 | 0 bytes | 0 bytes | 0 bytes | 0 bytes | | 32 | innodb/dict_persist_metadata_table_thread | 0 | 0 bytes | 0 bytes | 0 bytes | 0 bytes | | 34 | sql/signal_handler | 0 | 0 bytes | 0 bytes | 0 bytes | 0 bytes | +-----------+-------------------------------------------+--------------------+-------------------+-------------------+-------------------+-----------------+ 831 rows in set (2.48 sec)

Como se mencionó antes, el valor de ID de subproceso (thd_id) aquí puede hacer referencia a subprocesos en segundo plano del servidor o a conexiones de bases de datos. Si desea asignar valores de ID de subprocesos a ID de conexión a la base de datos, puede utilizar la tabla performance_schema.threads o la vista sys.processlist, donde conn_id es el ID de conexión.

mysql> SELECT thd_id,conn_id,user,db,command,state,time,last_wait FROM sys.processlist WHERE user='reinvent@10.0.4.4'; +--------+---------+-------------------+----------+---------+----------------+------+-------------------------------------------------+ | thd_id | conn_id | user | db | command | state | time | last_wait | +--------+---------+-------------------+----------+---------+----------------+------+-------------------------------------------------+ | 590 | 562 | reinvent@10.0.4.4 | sysbench | Execute | closing tables | 0 | wait/io/redo_log_flush | | 578 | 550 | reinvent@10.0.4.4 | sysbench | Sleep | NULL | 0 | idle | | 579 | 551 | reinvent@10.0.4.4 | sysbench | Execute | closing tables | 0 | wait/io/redo_log_flush | | 580 | 552 | reinvent@10.0.4.4 | sysbench | Execute | updating | 0 | wait/io/table/sql/handler | | 581 | 553 | reinvent@10.0.4.4 | sysbench | Execute | updating | 0 | wait/io/table/sql/handler | | 582 | 554 | reinvent@10.0.4.4 | sysbench | Sleep | NULL | 0 | idle | | 583 | 555 | reinvent@10.0.4.4 | sysbench | Sleep | NULL | 0 | idle | | 584 | 556 | reinvent@10.0.4.4 | sysbench | Execute | updating | 0 | wait/io/table/sql/handler | | 585 | 557 | reinvent@10.0.4.4 | sysbench | Execute | closing tables | 0 | wait/io/redo_log_flush | | 586 | 558 | reinvent@10.0.4.4 | sysbench | Execute | updating | 0 | wait/io/table/sql/handler | | 587 | 559 | reinvent@10.0.4.4 | sysbench | Execute | closing tables | 0 | wait/io/redo_log_flush | . . . . . . | 323 | 295 | reinvent@10.0.4.4 | sysbench | Sleep | NULL | 0 | idle | | 324 | 296 | reinvent@10.0.4.4 | sysbench | Execute | updating | 0 | wait/io/table/sql/handler | | 325 | 297 | reinvent@10.0.4.4 | sysbench | Execute | closing tables | 0 | wait/io/redo_log_flush | | 326 | 298 | reinvent@10.0.4.4 | sysbench | Execute | updating | 0 | wait/io/table/sql/handler | | 438 | 410 | reinvent@10.0.4.4 | sysbench | Execute | System lock | 0 | wait/lock/table/sql/handler | | 280 | 252 | reinvent@10.0.4.4 | sysbench | Sleep | starting | 0 | wait/io/socket/sql/client_connection | | 98 | 70 | reinvent@10.0.4.4 | sysbench | Query | freeing items | 0 | NULL | +--------+---------+-------------------+----------+---------+----------------+------+-------------------------------------------------+ 804 rows in set (5.51 sec)

Ahora detenemos la carga de trabajo de sysbench, lo que cierra las conexiones y libera memoria. Al volver a comprobar los eventos, podemos confirmar que la memoria se ha liberado, pero high_alloc nos sigue indicando cuál es el nivel máximo. La columna high_alloc puede resultar muy útil para identificar picos breves en el uso de memoria, cuando no se pueda identificar inmediatamente el uso de current_alloc, ya que muestra solo la memoria actualmente asignada.

mysql> SELECT * FROM sys.memory_global_by_current_bytes WHERE event_name='memory/sql/Prepared_statement::main_mem_root' LIMIT 10; +----------------------------------------------+---------------+---------------+-------------------+------------+------------+----------------+ | event_name | current_count | current_alloc | current_avg_alloc | high_count | high_alloc | high_avg_alloc | +----------------------------------------------+---------------+---------------+-------------------+------------+------------+----------------+ | memory/sql/Prepared_statement::main_mem_root | 17 | 253.80 KiB | 14.93 KiB | 512823 | 4.91 GiB | 10.04 KiB | +----------------------------------------------+---------------+---------------+-------------------+------------+------------+----------------+ 1 row in set (0.00 sec)

Si desea restablecer high_alloc, puede truncar las tablas de resumen de la memoria de performance_schema, pero esto restablece toda la instrumentación de la memoria. Para obtener más información, consulte Performance Schema general table characteristics en la documentación de MySQL.

En el siguiente ejemplo, podemos observar que high_alloc se restablece tras el truncado.

mysql> TRUNCATE `performance_schema`.`memory_summary_global_by_event_name`; Query OK, 0 rows affected (0.00 sec) mysql> SELECT * FROM sys.memory_global_by_current_bytes WHERE event_name='memory/sql/Prepared_statement::main_mem_root' LIMIT 10; +----------------------------------------------+---------------+---------------+-------------------+------------+------------+----------------+ | event_name | current_count | current_alloc | current_avg_alloc | high_count | high_alloc | high_avg_alloc | +----------------------------------------------+---------------+---------------+-------------------+------------+------------+----------------+ | memory/sql/Prepared_statement::main_mem_root | 17 | 253.80 KiB | 14.93 KiB | 17 | 253.80 KiB | 14.93 KiB | +----------------------------------------------+---------------+---------------+-------------------+------------+------------+----------------+ 1 row in set (0.00 sec)

Ejemplo 2: picos de memoria de transitorios

Los picos breves en el uso de memoria son algo habitual en un servidor de base de datos. Puede tratarse de descensos periódicos de la memoria que se puede liberar y que son difíciles solucionar mediante current_alloc en sys.memory_global_by_current_bytes, ya que la memoria ya se ha liberado.

nota

Si se han restablecido las estadísticas de Performance Schema o se ha reiniciado la instancia de la base de datos, esta información no estará disponible en sys o performance_schema. Para conservar esta información, recomendamos que configure la recopilación de métricas externas.

El siguiente gráfico de la métrica de os.memory.free en Supervisión mejorada muestra breves picos de 7 segundos en el uso de la memoria. Supervisión mejorada permite supervisar a intervalos de tan solo un segundo, lo que resulta perfecto para detectar picos transitorios como estos.

Gráfico que muestra picos transitorios de uso de memoria a lo largo del tiempo con un patrón periódico que indica posibles problemas de administración de la memoria.

Para ayudar a diagnosticar la causa del uso de memoria en este caso, podemos utilizar una combinación de high_alloc en las vistas de resumen de memoria de sys y las tablas de resumen de instrucciones de Performance Schema para tratar de identificar las sesiones y conexiones problemáticas.

Como era de esperar, dado que el uso de memoria no es elevado actualmente, no podemos detectar ningún infractor principal en la vista del esquema de sys bajo current_alloc.

mysql> SELECT * FROM sys.memory_global_by_current_bytes LIMIT 10; +-----------------------------------------------------------------------------+---------------+---------------+-------------------+------------+------------+----------------+ | event_name | current_count | current_alloc | current_avg_alloc | high_count | high_alloc | high_avg_alloc | +-----------------------------------------------------------------------------+---------------+---------------+-------------------+------------+------------+----------------+ | memory/innodb/hash0hash | 4 | 79.07 MiB | 19.77 MiB | 4 | 79.07 MiB | 19.77 MiB | | memory/innodb/os0event | 439372 | 60.34 MiB | 144 bytes | 439372 | 60.34 MiB | 144 bytes | | memory/performance_schema/events_statements_summary_by_digest | 1 | 40.28 MiB | 40.28 MiB | 1 | 40.28 MiB | 40.28 MiB | | memory/mysys/KEY_CACHE | 3 | 16.00 MiB | 5.33 MiB | 3 | 16.00 MiB | 5.33 MiB | | memory/performance_schema/events_statements_history_long | 1 | 14.34 MiB | 14.34 MiB | 1 | 14.34 MiB | 14.34 MiB | | memory/performance_schema/events_errors_summary_by_thread_by_error | 257 | 13.07 MiB | 52.06 KiB | 257 | 13.07 MiB | 52.06 KiB | | memory/performance_schema/events_statements_summary_by_thread_by_event_name | 1 | 11.81 MiB | 11.81 MiB | 1 | 11.81 MiB | 11.81 MiB | | memory/performance_schema/events_statements_summary_by_digest.digest_text | 1 | 9.77 MiB | 9.77 MiB | 1 | 9.77 MiB | 9.77 MiB | | memory/performance_schema/events_statements_history_long.digest_text | 1 | 9.77 MiB | 9.77 MiB | 1 | 9.77 MiB | 9.77 MiB | | memory/performance_schema/events_statements_history_long.sql_text | 1 | 9.77 MiB | 9.77 MiB | 1 | 9.77 MiB | 9.77 MiB | +-----------------------------------------------------------------------------+---------------+---------------+-------------------+------------+------------+----------------+ 10 rows in set (0.01 sec)

Al ampliar la vista para ordenar por high_alloc, ahora podemos ver que el componente memory/temptable/physical_ram es muy buen candidato en este caso. En su nivel más alto, consumía 515,00 MiB.

Tal y como indica su nombre, memory/temptable/physical_ram instrumenta el uso de memoria del motor de almacenamiento TEMP en MySQL, que se introdujo en MySQL 8.0. Para obtener más información sobre cómo utiliza MySQL las tablas temporales, consulte Internal temporary table use in MySQL en la documentación de MySQL.

nota

En este ejemplo, estamos utilizando la vista sys.x$memory_global_by_current_bytes.

mysql> SELECT event_name, format_bytes(current_alloc) AS "currently allocated", sys.format_bytes(high_alloc) AS "high-water mark" FROM sys.x$memory_global_by_current_bytes ORDER BY high_alloc DESC LIMIT 10; +-----------------------------------------------------------------------------+---------------------+-----------------+ | event_name | currently allocated | high-water mark | +-----------------------------------------------------------------------------+---------------------+-----------------+ | memory/temptable/physical_ram | 4.00 MiB | 515.00 MiB | | memory/innodb/hash0hash | 79.07 MiB | 79.07 MiB | | memory/innodb/os0event | 63.95 MiB | 63.95 MiB | | memory/performance_schema/events_statements_summary_by_digest | 40.28 MiB | 40.28 MiB | | memory/mysys/KEY_CACHE | 16.00 MiB | 16.00 MiB | | memory/performance_schema/events_statements_history_long | 14.34 MiB | 14.34 MiB | | memory/performance_schema/events_errors_summary_by_thread_by_error | 13.07 MiB | 13.07 MiB | | memory/performance_schema/events_statements_summary_by_thread_by_event_name | 11.81 MiB | 11.81 MiB | | memory/performance_schema/events_statements_summary_by_digest.digest_text | 9.77 MiB | 9.77 MiB | | memory/performance_schema/events_statements_history_long.sql_text | 9.77 MiB | 9.77 MiB | +-----------------------------------------------------------------------------+---------------------+-----------------+ 10 rows in set (0.00 sec)

En Ejemplo 1: uso elevado y continuo de memoria, comprobamos el uso de memoria actual de cada conexión para determinar qué conexión es responsable del uso de la memoria en cuestión. En este ejemplo, la memoria ya está liberada, por lo que comprobar el uso de memoria de las conexiones actuales no resulta útil.

Para profundizar y encontrar las instrucciones, los usuarios y los host infractores, utilizamos Performance Schema. Performance Schema contiene varias tablas de resumen de instrucciones divididas en diferentes dimensiones, como el nombre del evento, el resumen de instrucciones, el host, el subproceso y el usuario. Cada vista le permitirá profundizar en dónde se ejecutan determinadas instrucciones y qué es lo que hacen. Esta sección se centra en MAX_TOTAL_MEMORY, pero puede encontrar más información sobre todas las columnas disponibles en la documentación de las tablas de resumen de instrucciones de Performance Schema.

mysql> SHOW TABLES IN performance_schema LIKE 'events_statements_summary_%'; +------------------------------------------------------------+ | Tables_in_performance_schema (events_statements_summary_%) | +------------------------------------------------------------+ | events_statements_summary_by_account_by_event_name | | events_statements_summary_by_digest | | events_statements_summary_by_host_by_event_name | | events_statements_summary_by_program | | events_statements_summary_by_thread_by_event_name | | events_statements_summary_by_user_by_event_name | | events_statements_summary_global_by_event_name | +------------------------------------------------------------+ 7 rows in set (0.00 sec)

En primer lugar, comprobamos events_statements_summary_by_digest para ver MAX_TOTAL_MEMORY.

A partir de esto, podemos ver lo siguiente:

  • La consulta con el resumen 20676ce4a690592ff05debcffcbc26faeb76f22005e7628364d7a498769d0c4a parece ser una buena candidata para este uso de memoria. El valor de MAX_TOTAL_MEMORY es 537450710, que coincide con el nivel máximo que vimos para el evento de memory/temptable/physical_ram en sys.x$memory_global_by_current_bytes.

  • Se ha ejecutado cuatro veces (COUNT_STAR), la primera a las 04:08:34,943256 del 26 de marzo de 2024 y, la última, a las 04:43:06,998310 del 26 de marzo de 2024.

mysql> SELECT SCHEMA_NAME,DIGEST,COUNT_STAR,MAX_TOTAL_MEMORY,FIRST_SEEN,LAST_SEEN FROM performance_schema.events_statements_summary_by_digest ORDER BY MAX_TOTAL_MEMORY DESC LIMIT 5; +-------------+------------------------------------------------------------------+------------+------------------+----------------------------+----------------------------+ | SCHEMA_NAME | DIGEST | COUNT_STAR | MAX_TOTAL_MEMORY | FIRST_SEEN | LAST_SEEN | +-------------+------------------------------------------------------------------+------------+------------------+----------------------------+----------------------------+ | sysbench | 20676ce4a690592ff05debcffcbc26faeb76f22005e7628364d7a498769d0c4a | 4 | 537450710 | 2024-03-26 04:08:34.943256 | 2024-03-26 04:43:06.998310 | | NULL | f158282ea0313fefd0a4778f6e9b92fc7d1e839af59ebd8c5eea35e12732c45d | 4 | 3636413 | 2024-03-26 04:29:32.712348 | 2024-03-26 04:36:26.269329 | | NULL | 0046bc5f642c586b8a9afd6ce1ab70612dc5b1fd2408fa8677f370c1b0ca3213 | 2 | 3459965 | 2024-03-26 04:31:37.674008 | 2024-03-26 04:32:09.410718 | | NULL | 8924f01bba3c55324701716c7b50071a60b9ceaf17108c71fd064c20c4ab14db | 1 | 3290981 | 2024-03-26 04:31:49.751506 | 2024-03-26 04:31:49.751506 | | NULL | 90142bbcb50a744fcec03a1aa336b2169761597ea06d85c7f6ab03b5a4e1d841 | 1 | 3131729 | 2024-03-26 04:15:09.719557 | 2024-03-26 04:15:09.719557 | +-------------+------------------------------------------------------------------+------------+------------------+----------------------------+----------------------------+ 5 rows in set (0.00 sec)

Ahora que conocemos el resumen infractor, podemos obtener más detalles, como el texto de la consulta, el usuario que la ejecutó y dónde se ejecutó. Según el texto del resumen devuelto, podemos ver que se trata de una expresión común de tabla (CTE) que crea cuatro tablas temporales y realiza cuatro análisis de tablas, lo que resulta muy ineficiente.

mysql> SELECT SCHEMA_NAME,DIGEST_TEXT,QUERY_SAMPLE_TEXT,MAX_TOTAL_MEMORY,SUM_ROWS_SENT,SUM_ROWS_EXAMINED,SUM_CREATED_TMP_TABLES,SUM_NO_INDEX_USED FROM performance_schema.events_statements_summary_by_digest WHERE DIGEST='20676ce4a690592ff05debcffcbc26faeb76f22005e7628364d7a498769d0c4a'\G; *************************** 1. row *************************** SCHEMA_NAME: sysbench DIGEST_TEXT: WITH RECURSIVE `cte` ( `n` ) AS ( SELECT ? FROM `sbtest1` UNION ALL SELECT `id` + ? FROM `sbtest1` ) SELECT * FROM `cte` QUERY_SAMPLE_TEXT: WITH RECURSIVE cte (n) AS ( SELECT 1 from sbtest1 UNION ALL SELECT id + 1 FROM sbtest1) SELECT * FROM cte MAX_TOTAL_MEMORY: 537450710 SUM_ROWS_SENT: 80000000 SUM_ROWS_EXAMINED: 80000000 SUM_CREATED_TMP_TABLES: 4 SUM_NO_INDEX_USED: 4 1 row in set (0.01 sec)

Para obtener más información sobre la tabla events_statements_summary_by_digest y otras tablas de resumen de instrucciones de Performance Schema, consulte Statement summary tables en la documentación de MySQL.

También puede ejecutar una instrucción EXPLAIN o EXPLAIN ANALYZE para obtener más detalles.

nota

EXPLAIN ANALYZE puede aportar más información que EXPLAIN, pero también ejecuta la consulta, así que debe tener cuidado.

-- EXPLAIN mysql> EXPLAIN WITH RECURSIVE cte (n) AS (SELECT 1 FROM sbtest1 UNION ALL SELECT id + 1 FROM sbtest1) SELECT * FROM cte; +----+-------------+------------+------------+-------+---------------+------+---------+------+----------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+------------+------------+-------+---------------+------+---------+------+----------+----------+-------------+ | 1 | PRIMARY | <derived2> | NULL | ALL | NULL | NULL | NULL | NULL | 19221520 | 100.00 | NULL | | 2 | DERIVED | sbtest1 | NULL | index | NULL | k_1 | 4 | NULL | 9610760 | 100.00 | Using index | | 3 | UNION | sbtest1 | NULL | index | NULL | k_1 | 4 | NULL | 9610760 | 100.00 | Using index | +----+-------------+------------+------------+-------+---------------+------+---------+------+----------+----------+-------------+ 3 rows in set, 1 warning (0.00 sec) -- EXPLAIN format=tree mysql> EXPLAIN format=tree WITH RECURSIVE cte (n) AS (SELECT 1 FROM sbtest1 UNION ALL SELECT id + 1 FROM sbtest1) SELECT * FROM cte\G; *************************** 1. row *************************** EXPLAIN: -> Table scan on cte (cost=4.11e+6..4.35e+6 rows=19.2e+6) -> Materialize union CTE cte (cost=4.11e+6..4.11e+6 rows=19.2e+6) -> Index scan on sbtest1 using k_1 (cost=1.09e+6 rows=9.61e+6) -> Index scan on sbtest1 using k_1 (cost=1.09e+6 rows=9.61e+6) 1 row in set (0.00 sec) -- EXPLAIN ANALYZE mysql> EXPLAIN ANALYZE WITH RECURSIVE cte (n) AS (SELECT 1 from sbtest1 UNION ALL SELECT id + 1 FROM sbtest1) SELECT * FROM cte\G; *************************** 1. row *************************** EXPLAIN: -> Table scan on cte (cost=4.11e+6..4.35e+6 rows=19.2e+6) (actual time=6666..9201 rows=20e+6 loops=1) -> Materialize union CTE cte (cost=4.11e+6..4.11e+6 rows=19.2e+6) (actual time=6666..6666 rows=20e+6 loops=1) -> Covering index scan on sbtest1 using k_1 (cost=1.09e+6 rows=9.61e+6) (actual time=0.0365..2006 rows=10e+6 loops=1) -> Covering index scan on sbtest1 using k_1 (cost=1.09e+6 rows=9.61e+6) (actual time=0.0311..2494 rows=10e+6 loops=1) 1 row in set (10.53 sec)

Pero, ¿quién lo ha ejecutado? Podemos ver en Performance Schema que el usuario destructive_operator tenía un MAX_TOTAL_MEMORY de 537450710, lo que de nuevo coincide con los resultados anteriores.

nota

Performance Schema se almacena en la memoria, por lo que no se debe confiar en él como la única fuente de auditoría. Si necesita mantener un historial de las instrucciones ejecutadas y qué usuarios las ejecutan, le recomendamos que habilite Aurora Advanced Auditing. Si también necesita mantener información sobre el uso de la memoria, le recomendamos que configure la supervisión para exportar y almacenar estos valores.

mysql> SELECT USER,EVENT_NAME,COUNT_STAR,MAX_TOTAL_MEMORY FROM performance_schema.events_statements_summary_by_user_by_event_name ORDER BY MAX_CONTROLLED_MEMORY DESC LIMIT 5; +----------------------+---------------------------+------------+------------------+ | USER | EVENT_NAME | COUNT_STAR | MAX_TOTAL_MEMORY | +----------------------+---------------------------+------------+------------------+ | destructive_operator | statement/sql/select | 4 | 537450710 | | rdsadmin | statement/sql/select | 4172 | 3290981 | | rdsadmin | statement/sql/show_tables | 2 | 3615821 | | rdsadmin | statement/sql/show_fields | 2 | 3459965 | | rdsadmin | statement/sql/show_status | 75 | 1914976 | +----------------------+---------------------------+------------+------------------+ 5 rows in set (0.00 sec) mysql> SELECT HOST,EVENT_NAME,COUNT_STAR,MAX_TOTAL_MEMORY FROM performance_schema.events_statements_summary_by_host_by_event_name WHERE HOST != 'localhost' AND COUNT_STAR>0 ORDER BY MAX_CONTROLLED_MEMORY DESC LIMIT 5; +------------+----------------------+------------+------------------+ | HOST | EVENT_NAME | COUNT_STAR | MAX_TOTAL_MEMORY | +------------+----------------------+------------+------------------+ | 10.0.8.231 | statement/sql/select | 4 | 537450710 | +------------+----------------------+------------+------------------+ 1 row in set (0.00 sec)

Ejemplo 3: la memoria liberable cae continuamente y no se recupera

El motor de base de datos InnoDB emplea una variedad de eventos de seguimiento de memoria especializados para diferentes componentes. Estos eventos específicos permiten un seguimiento detallado del uso de la memoria en los principales subsistemas de InnoDB, por ejemplo:

  • memory/innodb/buf0buf: dedicado a supervisar las asignaciones de memoria para el grupo de búferes de InnoDB.

  • memory/innodb/ibuf0ibuf: realiza un seguimiento específico de los cambios de memoria relacionados con el búfer de cambios de InnoDB.

Para identificar los principales consumidores de memoria, podemos consultar sys.memory_global_by_current_bytes:

mysql> SELECT event_name,current_alloc FROM sys.memory_global_by_current_bytes LIMIT 10; +-----------------------------------------------------------------+---------------+ | event_name | current_alloc | +-----------------------------------------------------------------+---------------+ | memory/innodb/memory | 5.28 GiB | | memory/performance_schema/table_io_waits_summary_by_index_usage | 495.00 MiB | | memory/performance_schema/table_shares | 488.00 MiB | | memory/sql/TABLE_SHARE::mem_root | 388.95 MiB | | memory/innodb/std | 226.88 MiB | | memory/innodb/fil0fil | 198.49 MiB | | memory/sql/binlog_io_cache | 128.00 MiB | | memory/innodb/mem0mem | 96.82 MiB | | memory/innodb/dict0dict | 96.76 MiB | | memory/performance_schema/rwlock_instances | 88.00 MiB | +-----------------------------------------------------------------+---------------+ 10 rows in set (0.00 sec)

Los resultados muestran que memory/innodb/memory es el principal consumidor, ya que utiliza 5,28 GiB de la memoria asignada actualmente. Este evento sirve como categoría para las asignaciones de memoria entre varios componentes de InnoDB no asociados a eventos de espera más específicos, como los memory/innodb/buf0buf mencionados anteriormente.

Una vez establecido que los componentes de InnoDB son los principales consumidores de memoria, podemos profundizar en los detalles con el siguiente comando de MySQL:

SHOW ENGINE INNODB STATUS \G;

El comando SHOW ENGINE INNODB STATUS proporciona un informe de estado completo del motor de almacenamiento de InnoDB, que incluye estadísticas detalladas de uso de memoria de los diferentes componentes de InnoDB. Puede ayudar a identificar qué estructuras u operaciones específicas de InnoDB consumen más memoria. Para obtener información, consulte InnoDB in-memory structures en la documentación de MySQL.

Al analizar la sección BUFFER POOL AND MEMORY del informe de estado de InnoDB, vemos que se asignan 5 051 647 748 bytes (4,7 GiB) a la caché de objetos del diccionario, que representa el 89 % de la memoria rastreada por memory/innodb/memory.

---------------------- BUFFER POOL AND MEMORY ---------------------- Total large memory allocated 0 Dictionary memory allocated 5051647748 Buffer pool size 170512 Free buffers 142568 Database pages 27944 Old database pages 10354 Modified db pages 6 Pending reads 0

La caché de objetos del diccionario es una caché global compartida que almacena en la memoria los objetos del diccionario de datos a los que se ha accedido anteriormente para permitir la reutilización de los objetos y mejorar el rendimiento. La elevada asignación de memoria a la caché de objetos del diccionario sugiere que hay una gran cantidad de objetos de base de datos en la caché del diccionario de datos.

Ahora que sabemos que la caché del diccionario de datos es un consumidor principal, procedemos a inspeccionar la caché del diccionario de datos para ver si hay tablas abiertas. Para encontrar el número de tablas en la caché de definiciones de tablas, consulte la variable de estado global open_table_definitions.

mysql> show global status like 'open_table_definitions'; +------------------------+-------+ | Variable_name | Value | +------------------------+-------+ | Open_table_definitions | 20000 | +------------------------+-------+ 1 row in set (0.00 sec)

Para obtener más información, consulte How MySQL Opens and Closes Tables en la documentación de MySQL.

Puede limitar el número de definiciones de tablas en la caché del diccionario de datos limitando el parámetro table_definition_cache en el clúster de base de datos o en el grupo de parámetros de la instancia de base de datos. Para Aurora MySQL, este valor sirve como límite flexible para el número de tablas en la caché de definición de tablas. El valor predeterminado depende de la clase de instancia y se establece de la siguiente manera:

LEAST({DBInstanceClassMemory/393040}, 20000)

Cuando el número de tablas supera el límite de table_definition_cache, el mecanismo utilizado menos recientemente (LRU) expulsa y elimina las tablas de la memoria caché. Sin embargo, las tablas involucradas en relaciones de clave externa no se incluyen en la lista LRU, lo que impide su eliminación.

En nuestro escenario actual, ejecutamos FLUSH TABLES para borrar la caché de definiciones de tablas. Esta acción provoca una caída significativa de la variable de estado global Open_table_definitions, de 20 000 a 12, tal como se muestra a continuación:

mysql> show global status like 'open_table_definitions'; +------------------------+-------+ | Variable_name | Value | +------------------------+-------+ | Open_table_definitions | 12 | +------------------------+-------+ 1 row in set (0.00 sec)

A pesar de esta reducción, observamos que la asignación de memoria para memory/innodb/memory sigue siendo alta, de 5,18 GiB, y la memoria del diccionario asignada también permanece sin cambios. Esto queda patente en los siguientes resultados de la consulta:

mysql> SELECT event_name,current_alloc FROM sys.memory_global_by_current_bytes LIMIT 10; +-----------------------------------------------------------------+---------------+ | event_name | current_alloc | +-----------------------------------------------------------------+---------------+ | memory/innodb/memory | 5.18 GiB | | memory/performance_schema/table_io_waits_summary_by_index_usage | 495.00 MiB | | memory/performance_schema/table_shares | 488.00 MiB | | memory/sql/TABLE_SHARE::mem_root | 388.95 MiB | | memory/innodb/std | 226.88 MiB | | memory/innodb/fil0fil | 198.49 MiB | | memory/sql/binlog_io_cache | 128.00 MiB | | memory/innodb/mem0mem | 96.82 MiB | | memory/innodb/dict0dict | 96.76 MiB | | memory/performance_schema/rwlock_instances | 88.00 MiB | +-----------------------------------------------------------------+---------------+ 10 rows in set (0.00 sec)
---------------------- BUFFER POOL AND MEMORY ---------------------- Total large memory allocated 0 Dictionary memory allocated 5001599639 Buffer pool size 170512 Free buffers 142568 Database pages 27944 Old database pages 10354 Modified db pages 6 Pending reads 0

Este elevado uso de memoria de forma persistente se puede atribuir a las tablas implicadas en relaciones de claves externas. Estas tablas no se incluyen en la lista LRU para su eliminación, lo que explica por qué la asignación de memoria sigue siendo alta incluso después de vaciar la caché de definiciones de tablas.

Para solucionar este problema:

  1. Revise y optimice el esquema de su base de datos, especialmente las relaciones de claves externas.

  2. Considere la posibilidad de cambiar a una clase de instancia de base de datos más grande que tenga más memoria para acomodar los objetos de diccionario.

Si sigue estos pasos y comprende los patrones de asignación de memoria, podrá administrar mejor el uso de la memoria en su instancia de base de datos de Aurora MySQL y evitar posibles problemas de rendimiento debido a la presión sobre la memoria.