Procedimientos recomendados para solucionar problemas de Azure Database for MySQL: servidor flexible

SE APLICA A: Azure Database for MySQL: servidor único Azure Database for MySQL: servidor flexible

Importante

El servidor único de Azure Database for MySQL está en la ruta de retirada. Se recomienda encarecidamente actualizar al servidor flexible de Azure Database for MySQL. Para más información sobre la migración al servidor flexible de Azure Database for MySQL, consulte ¿Qué ocurre con Azure Database for MySQL con servidor único?

Use las secciones siguientes para mantener las bases de datos de servidor flexibles de Azure Database for MySQL ejecutándose sin problemas y usar esta información como principios rectores para garantizar que los esquemas están diseñados de forma óptima y proporcionar el mejor rendimiento para las aplicaciones.

Comprobación del número de índices

En un entorno de base de datos con mucha actividad, puede observar un uso elevado de operaciones de E/S, que puede ser un indicador de patrones deficientes de acceso a los datos. Los índices no utilizados pueden afectar negativamente al rendimiento, ya que consumen espacio en disco y caché, y ralentizan las operaciones de escritura (INSERT/DELETE/UPDATE). Los índices no utilizados consumen espacio de almacenamiento adicional innecesario y aumentan el tamaño de la copia de seguridad.

Antes de quitar cualquier índice, asegúrese de recopilar suficiente información para comprobar que ya no está en uso. Esta comprobación puede ayudarle a evitar quitar por accidente un índice que es crítico para una consulta que se ejecuta solo trimestral o anualmente. Además, asegúrese de comprobar si un índice se usa para aplicar unicidad u ordenación.

Nota

Recuerde revisar los índices periódicamente y realizar las actualizaciones necesarias en función de las modificaciones en los datos de las tablas.

SELECT object_schema, object_name, index_name FROM performance_schema.table_io_waits_summary_by_index_usage WHERE index_name IS NOT NULL AND count_star = 0 ORDER BY object_schema, object_name;

o

use information_schema; select tables.table_name, statistics.index_name, statistics.cardinality, tables.table_rows from tables join statistics on (statistics.table_name = tables.table_name and statistics.table_schema = '<YOUR DATABASE NAME HERE>' and ((tables.table_rows / statistics.cardinality) > 1000));

Enumeración de los índices más ocupados del servidor

La salida de la consulta siguiente proporciona información sobre los índices más usados en todas las tablas y esquemas del servidor de bases de datos. Esta información es útil para identificar la relación entre escrituras y lecturas en cada índice y los números de latencia de las lecturas, así como las operaciones de escritura individuales, lo que puede indicar que se requiere un ajuste adicional en la tabla subyacente y las consultas dependientes.

SELECT 
object_schema AS table_schema, 
object_name AS table_name, 
index_name, count_star AS all_accesses, 
count_read, 
count_write, 
Concat(Truncate(count_read / count_star * 100, 0), ':', 
Truncate(count_write / count_star * 100, 0)) AS read_write_ratio, 
 count_fetch AS rows_selected , 
 count_insert AS rows_inserted, 
 count_update AS rows_updated, 
 count_delete AS rows_deleted, 
 Concat(Round(sum_timer_wait / 1000000000000, 2), ' s') AS total_latency , 
 Concat(Round(sum_timer_fetch / 1000000000000, 2), ' s') AS select_latency, 
 Concat(Round(sum_timer_insert / 1000000000000, 2), ' s') AS insert_latency, 
Concat(Round(sum_timer_update / 1000000000000, 2), ' s') AS update_latency, 
 Concat(Round(sum_timer_delete / 1000000000000, 2), ' s') AS  delete_latency 
FROM performance_schema.table_io_waits_summary_by_index_usage 
WHERE index_name IS NOT NULL AND count_star > 0 
ORDER BY sum_timer_wait DESC 

Revisión del diseño de la clave principal

El servidor flexible de Azure Database for MySQL usa el motor de almacenamiento InnoDB para todas las tablas nontemporary. Con InnoDB, los datos se almacenan en un índice agrupado con una estructura de árbol B. La tabla se organiza físicamente en función de los valores de clave principal, lo que significa que las filas se almacenan en el orden de la clave principal.

Cada entrada de clave secundaria de una tabla de InnoDB contiene un puntero al valor de la clave principal donde se almacenan los datos. Dicho de otro modo, una entrada de índice secundaria contiene una copia del valor de la clave principal a la que apunta la entrada. Por tanto, las opciones de clave principal tienen un efecto directo en la cantidad de sobrecarga de almacenamiento en las tablas.

Si una clave se deriva de datos reales (por ejemplo, nombre de usuario, correo electrónico, SSN, etc.), se denomina clave natural. Si una clave es artificial y no se deriva de datos (por ejemplo, un entero incrementado automáticamente), se conoce como clave sintética o clave suplente.

En general, se recomienda evitar el uso de claves principales naturales. Estas claves suelen ser muy anchas y contener valores largos de una o varias columnas. Esto, a su vez, puede introducir una sobrecarga de almacenamiento importante con el valor de clave principal que se copia en cada entrada de clave secundaria. Además, las claves naturales no suelen seguir un orden predefinido, lo que reduce drásticamente el rendimiento y provoca la fragmentación de las páginas cuando se insertan o actualizan filas. Para evitar estos problemas, en lugar de usar claves naturales, use claves suplentes, que aumentan monotónicamente. Una columna (big)integer de incremento automático es un buen ejemplo de clave suplente que aumenta de forma monotónica. Si necesita una determinada combinación de columnas, declare esas columnas como una clave secundaria única.

Durante las fases iniciales de creación de una aplicación, es posible que no se imagine por adelantado el momento en que su tabla empiece a acercarse a los dos mil millones de filas. Por eso, quizá opte por usar un entero de 4 bytes con signo para el tipo de datos de una columna de identificador (clave principal). Asegúrese de comprobar todas las claves principales de la tabla y cambie al uso de columnas de enteros de 8 bytes (BIGINT) para dar cabida a un posible volumen o crecimiento elevados.

Nota:

Para obtener más información sobre los tipos de datos y sus valores máximos, vea Tipos de datos en el manual de consulta de MySQL.

Uso de índices de cobertura

En la sección anterior se explica cómo se organizan los índices de MySQL en árboles B y en un índice agrupado. Los nodos hoja contienen las páginas de datos de la tabla subyacente. Los índices secundarios tienen la misma estructura de árbol B que los índices agrupados y puede definirlos en una tabla o una vista con un índice agrupado o un montón. Cada fila del índice secundario contiene el valor de clave no agrupada y un localizador de fila. Este localizador apunta a la fila de datos del índice clúster o el montón que contiene el valor de clave. Como resultado, cualquier búsqueda que implique un índice secundario debe ir desde el nodo raíz a través de los nodos de rama hasta el nodo hoja correcto para tomar el valor de la clave principal. Después, el sistema ejecuta una lectura de E/S aleatoria en el índice de la clave principal (una vez más, yendo desde el nodo raíz a través de los nodos de rama hasta el nodo hoja correcto) para obtener la fila de datos.

Para evitar esta lectura de E/S aleatoria adicional en el índice de la clave principal con el fin de obtener la fila de datos, use un índice de cobertura, que incluye todos los campos que la consulta requiere. Por lo general, el uso de este enfoque es bueno para cargas de trabajo de entrada/salida y cargas de trabajo almacenadas en caché. Por tanto, como procedimiento recomendado, use índices de cobertura, porque caben en la memoria y son más pequeños y eficaces para leerlos que examinar todas las filas.

Imagine, por ejemplo, una tabla que está usando para intentar encontrar a todos los empleados que se incorporaron a la empresa después del 1 de enero de 2000.

mysql> show create table employee\G
*************************** 1. row ***************************
       Table: employee
Create Table: CREATE TABLE `employee` (
  `empid` int(11) NOT NULL AUTO_INCREMENT,
  `fname` varchar(10) DEFAULT NULL,
  `lname` varchar(10) DEFAULT NULL,
  `joindate` datetime DEFAULT NULL,
  `department` varchar(10) DEFAULT NULL,
  PRIMARY KEY (`empid`)
  ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1
1 row in set (0.00 sec)`

`mysql> select empid, fname, lname from employee where joindate > '2000-01-01';

Si ejecuta un plan EXPLAIN en esta consulta, observará que actualmente no se usan índices y que se usa una cláusula where sola para filtrar los registros de empleados.

mysql> EXPLAIN select empid, fname, lname from employee where joindate > '2000-01-01'\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: employee
   partitions: NULL
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 3
     filtered: 33.33
        Extra: Using where
1 row in set, 1 warning (0.01 sec)

Sin embargo, si agrega un índice que cubra la columna en la cláusula where, junto con las columnas proyectadas, verá que se usa el índice para localizar las columnas de una forma mucho más rápida y eficaz.

mysql> CREATE INDEX cvg_idx_ex ON employee (joindate, empid, fname, lname);

Ahora, si ejecuta el plan EXPLAIN en la misma consulta, el valor "Using Index" aparece en el campo "Extra". Esto significa que InnoDB ejecuta la consulta con el índice que hemos creado antes, lo que confirma que se trata de un índice de cobertura.

mysql> EXPLAIN select empid, fname, lname from employee where joindate > '2000-01-01'\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: employee
   partitions: NULL
         type: range
possible_keys: cvg_idx_ex
          key: cvg_idx_ex
      key_len: 6
          ref: NULL
         rows: 1
     filtered: 100.00
        Extra: Using where; Using index
1 row in set, 1 warning (0.01 sec)

Nota

Es importante elegir el orden correcto de las columnas del índice de cobertura para atender correctamente la consulta. La regla general es elegir las columnas para filtrar primero (cláusula WHERE); después, ordenar o agrupar (ORDER BY y GROUP BY); y, por último, la proyección de datos (SELECT).

En el ejemplo anterior, hemos visto que tener un índice de cobertura para una consulta proporciona vías de recuperación de registros más eficaces y optimiza el rendimiento en un entorno de base de datos con un elevado número de operaciones simultáneas.

Pasos siguientes

Para encontrar respuestas de otros compañeros a sus preguntas, o bien publicar o responder nuevas preguntas, visite Stack Overflow.