Optimizar el uso elevado de CPU en las instancias

El alto uso de la CPU en una instancia puede deberse a varios motivos, como aumento en las cargas de trabajo, transacciones pesadas, consultas lentas y transacciones de ejecución prolongada.

El recomendador de instancias con aprovisionamiento insuficiente analiza el uso de la CPU. Si el nivel de uso de la CPU es igual o superior al 95 % durante un período considerable en los últimos 30 días, el recomendador le avisa y le proporciona información adicional para ayudarle a resolver el problema.

Este documento explica cómo revisar y optimizar una instancia de Cloud SQL para MySQL si el recomendador de instancias con aprovisionamiento insuficiente identifica que dicha instancia tiene un alto uso de CPU.

Recomendaciones

El uso de la CPU aumenta proporcionalmente con la carga de trabajo. Para reducirlo, revise las consultas en ejecución y optimícelas. A continuación, se indican algunos pasos para verificar el consumo de CPU.

  1. Comprobar Threads_running y Threads_connected

    Utilice la siguiente consulta para ver el número de subprocesos activos:

    > SHOW STATUS like 'Threads_%';
    

    Threads_running es un subconjunto de Threads_connected . El resto de los hilos están inactivos. Un aumento en Threads_running contribuiría a un mayor uso de la CPU. Es recomendable comprobar qué se está ejecutando en esos hilos.

  2. Comprobar estados de consulta

    Ejecute el comando SHOW PROCESSLIST para ver las consultas en curso. Devuelve todos los subprocesos conectados en orden y su instrucción SQL activa.

    mysql> SHOW [FULL] PROCESSLIST;
    

    Preste atención a las columnas de estado y duración. Compruebe si hay varias consultas bloqueadas en el mismo estado.

    • Si muchos subprocesos muestran Updating , podría haber una contención de bloqueo de registros. Consulte el siguiente paso.
    • Si muchos subprocesos muestran " Waiting bloqueo de metadatos de tabla", revise la consulta para identificar la tabla y luego busque un DDL (como ALTER TABLE ) que pueda contener el bloqueo de metadatos. Un DDL también podría estar esperando el bloqueo de metadatos de tabla si una consulta temprana, como una SELECT query de larga duración, lo contiene.
  3. Comprobar si hay contención de bloqueo de registros

    Cuando las transacciones mantienen bloqueos en registros de índice populares, bloquean otras transacciones que solicitan los mismos bloqueos. Esto podría generar un efecto en cadena y provocar el bloqueo de varias solicitudes y un aumento en el valor de Threads_running . Para diagnosticar la contención de bloqueos, utilice la tabla information_schema.innodb_lock_waits .

    La siguiente consulta enumera cada transacción de bloqueo y la cantidad de transacciones bloqueadas asociadas.

    SELECT 
      t.trx_id, 
      t.trx_state, 
      t.trx_started, 
      COUNT(distinct w.requesting_trx_id) AS blocked_trxs
    FROM 
      information_schema.innodb_lock_waits w 
    INNER JOIN information_schema.innodb_trx t
       ON t.trx_id = w.blocking_trx_id 
    GROUP BY t.trx_id,t.trx_state, t.trx_started
    ORDER BY t.trx_id;
    

    Tanto un único DML grande como muchos DML pequeños simultáneos pueden causar contenciones de bloqueo de filas. Puede optimizar esto desde la aplicación siguiendo estos pasos:

    • Evite las transacciones largas porque los bloqueos de filas se mantienen hasta que finaliza la transacción.
    • Divida un único DML grande en DML más pequeños.
    • Divida un DML de una sola fila en fragmentos pequeños.
    • Minimice la contención entre subprocesos; por ejemplo, si el código de la aplicación utiliza un grupo de conexiones, asigne un rango de ID al mismo subproceso.
  4. Encuentra transacciones de larga duración

    • Utilice SHOW ENGINE INNODB STATUS

      En la sección TRANSACCIONES podrás ver todas las transacciones abiertas ordenadas de más antigua a más antigua.

      mysql> SHOW ENGINE INNODB STATUS\G
      ……
      ------------
      TRANSACTIONS
      ------------
      …
      ---TRANSACTION 245762, ACTIVE 262 sec
      2 lock struct(s), heap size 1136, 1 row lock(s), undo log entries 1
      MySQL thread id 9210, OS thread handle 140262286128896, query id 202218 localhost root
      

      Comience con las transacciones más antiguas y encuentre respuestas a las siguientes preguntas:

      • ¿Cuánto tiempo llevan realizándose estas transacciones?
      • ¿Cuántas estructuras de bloqueo y bloqueos de fila hay presentes?
      • ¿Cuántas entradas de registro de deshacer hay?
      • ¿Cuáles son los hosts y usuarios que se conectan?
      • ¿Qué es la declaración SQL en curso?
    • Utilice information_schema.innodb_trx

      Si se truncó SHOW ENGINE INNODB STATUS , una forma alternativa de examinar todas las transacciones abiertas es utilizar la tabla information_schema.innodb_trx :

      SELECT
       trx_id, trx_state, 
       timestampdiff(second, trx_started, now()) AS active_secs, 
       timestampdiff(second, trx_wait_started, now()) AS wait_secs, trx_tables_in_use,
       trx_tables_locked, 
       trx_lock_structs, 
       trx_rows_locked, 
       trx_rows_modified, 
       trx_query 
      FROM information_schema.innodb_trx
      

    Si las transacciones muestran los extractos de larga duración actuales, puede decidir detenerlas para reducir la carga en el servidor o esperar a que se completen las transacciones críticas. Si las transacciones anteriores no muestran actividad, vaya al siguiente paso para consultar el historial de transacciones.

  5. Comprobar las sentencias SQL de las transacciones de larga ejecución

    • Utilice performance_schema

      Para usar performance_schema , primero debe activarlo. Este cambio requiere reiniciar la instancia. Una vez performance_schema , compruebe que los instrumentos y consumidores estén habilitados:

      SELECT * FROM setup_consumers where name like 'events_statements_history';
      SELECT * FROM setup_instruments where name like 'statement/sql/%';
      
      

      Si no están habilitados, habilítelos:

      UPDATE setup_instruments SET ENABLED = 'YES', timed = 'YES' WHERE NAME LIKE 'statement/%';
      UPDATE setup_consumers SET ENABLED = 'YES' WHERE NAME LIKE 'events_statements%';
      

      De forma predeterminada, cada hilo conserva los últimos 10 eventos definidos por performance_schema_events_statements_history_size . Estos suelen ser suficientes para localizar la transacción en el código de la aplicación. Este parámetro no es dinámico.

      Con el mysql thread id , que es processlist_id , consulta los eventos del historial:

      SELECT 
       t.thread_id, 
       event_name, 
       sql_text, 
       rows_affected, 
       rows_examined, 
       processlist_id, 
       processlist_time, 
       processlist_state 
      FROM events_statements_history h 
      INNER JOIN threads t 
      ON h.thread_id = t.thread_id 
      WHERE processlist_id = <mysql thread id>
      ORDER BY event_id;
      
    • Usar registro de consultas lentas

      Para la depuración, puede capturar todas las consultas que tardaron más de N segundos en el registro de consultas lentas. Puede habilitar los registros de consultas lentas editando la configuración de la instancia en la página de instancias.Google Cloud consola o gcloud CLI y luego vea los registros usando el visor de registros en elGoogle Cloud consola o gloud CLI .

  6. Comprobar la contención del semáforo

    En un entorno concurrente, el mutex y el bloqueo de lectura/escritura en recursos compartidos pueden ser el punto de contención, lo que ralentiza el rendimiento del servidor. Además, si el tiempo de espera del semáforo supera los 600 segundos, el sistema puede colapsar para salir del bloqueo.

    Para ver la contención del semáforo, utilice el siguiente comando:

    mysql> SHOW ENGINE INNODB STATUS\G
    ----------
    SEMAPHORES
    ----------
    ...
      --Thread 140396021667584 has waited at row0purge.cc line 862 for 241.00 seconds the semaphore:
      S-lock on RW-latch at 0x30c03e8 created in file dict0dict.cc line 1183
      a writer (thread id 140395996489472) has reserved it in mode  exclusive
      number of readers 0, waiters flag 1, lock_word: 0
      Last time read locked in file row0purge.cc line 862
      Last time write locked in file /build/mysql-5.7-FFKPr6/mysql-5.7-5.7.22/storage/innobase/dict/dict0stats.cc line 2376
    ...
    

    Con cada espera de semáforo, la primera línea muestra el hilo en espera, el semáforo específico y el tiempo de espera. Si se producen esperas frecuentes al ejecutar repetidamente SHOW ENGINE INNODB STATUS , especialmente si superan unos pocos segundos, significa que el sistema está experimentando cuellos de botella de concurrencia.

    Hay diferentes puntos de contención en diferentes cargas de trabajo y configuraciones.

    Cuando los semáforos suelen estar en btr0sea.c, la indexación hash adaptativa podría ser la fuente de contención. Intente desactivarla usando Google Cloud consola o gcloud CLI .

  7. Optimizar consultas SELECT largas

    Primero, revise la consulta. Identifique el objetivo de la consulta y la mejor manera de obtener los resultados. El mejor plan de consulta es el que minimiza el acceso a los datos.

    • Compruebe el plan de ejecución de la consulta:
    mysql> EXPLAIN <the query>;
    

    Consulte la documentación de MySQL para aprender a interpretar la salida y evaluar la eficiencia de la consulta.

    • Utilice el índice correcto

    Compruebe la columna clave para ver si se utiliza el índice esperado. De lo contrario, actualice las estadísticas del índice:

    mysql> analyze table <table_name> 
    

    Aumente el número de páginas de muestra utilizadas para calcular las estadísticas de índice. Para obtener más información, consulte la documentación de MySQL .

    • Aprovechar al máximo el índice

    Al usar un índice multicolumna, revise las columnas key_len para ver si el índice se utiliza completamente para filtrar los registros. Las columnas más a la izquierda deben ser comparaciones de igualdad, y el índice puede usarse hasta la primera condición de rango, inclusive.

    • Utilice las sugerencias del optimizador

    Otra forma de garantizar el índice correcto es utilizar la sugerencia de índice y la sugerencia para el orden de unión de la tabla .

  8. Evite una lista de historial larga con READ COMMITTED

    El historial es la lista de transacciones no purgadas en el espacio de tablas de deshacer. El nivel de aislamiento predeterminado de una transacción es REPEATABLE READ , que requiere que la transacción lea la misma instantánea durante toda su duración. Por lo tanto, una consulta SELECT bloquea la purga de los registros de deshacer generados desde el inicio de la consulta (o transacción). Un historial extenso ralentiza el rendimiento de la consulta. Una forma de evitar generar un historial extenso es cambiar el nivel de aislamiento de la transacción a READ COMMITTED . Con READ COMMITTED , ya no es necesario mantener el historial para una vista de lectura consistente. Puede cambiar el nivel de aislamiento de la transacción globalmente para todas las sesiones, para una sola sesión o para la siguiente transacción. Para obtener más información, consulte la documentación de MySQL .

  9. Ajustar la configuración del servidor

    Hay mucho que decir sobre la configuración del servidor. Si bien la historia completa queda fuera del alcance de este documento, cabe mencionar que el servidor también reporta diversas variables de estado que ofrecen indicios sobre el correcto funcionamiento de las configuraciones relacionadas. Por ejemplo:

    • Ajuste thread_cache_size si Threads_created/Connections es grande. Una caché de subprocesos adecuada reduciría el tiempo de creación de subprocesos y facilitaría la carga de trabajo con alta concurrencia.
    • Ajuste table_open_cache si Table_open_cache_misses/Table_open_cache_hits no es trivial. Tener tablas en la caché de tablas ahorra tiempo de ejecución de consultas y podría marcar la diferencia en un entorno altamente concurrente.
  10. Finalizar una conexión no deseada

    Puede detener la consulta si esta parece inválida o ya no es necesaria. Para saber cómo identificar y finalizar el hilo de MySQL, consulte Administrar conexiones de base de datos .

Por último, si el uso de la CPU sigue siendo alto y las consultas generan tráfico necesario, considere aumentar los recursos de la CPU en su instancia para evitar caídas o tiempos de inactividad de la base de datos.

¿Qué sigue?