sql >> Base de Datos >  >> RDS >> MariaDB

Maximización de la eficiencia de las consultas de la base de datos para MySQL:primera parte

Las consultas lentas, las consultas ineficientes o las consultas de ejecución prolongada son problemas que afectan regularmente a los administradores de bases de datos. Siempre están en todas partes, pero son una parte inevitable de la vida de cualquier persona responsable de administrar una base de datos.

Un diseño deficiente de la base de datos puede afectar la eficiencia de la consulta y su rendimiento. La falta de conocimiento o el uso inadecuado de las llamadas a funciones, los procedimientos almacenados o las rutinas también pueden provocar la degradación del rendimiento de la base de datos e incluso dañar todo el clúster de la base de datos MySQL.

Para una replicación maestro-esclavo, una causa muy común de estos problemas son las tablas que carecen de índices primarios o secundarios. Esto provoca un retraso del esclavo que puede durar mucho tiempo (en el peor de los casos).

En esta serie de blogs de dos partes, le daremos un curso de actualización sobre cómo abordar la maximización de sus consultas de base de datos en MySQL para impulsar una mejor eficiencia y rendimiento.

Agregue siempre un índice único a su tabla

Las tablas que no tienen claves primarias o únicas normalmente crean grandes problemas cuando los datos aumentan. Cuando esto sucede, una simple modificación de datos puede detener la base de datos. Si no se han aplicado los índices adecuados y se ha aplicado una instrucción UPDATE o DELETE a la tabla en particular, MySQL elegirá una exploración completa de la tabla como plan de consulta. Eso puede causar una E/S de disco alta para lecturas y escrituras y degrada el rendimiento de su base de datos. Vea un ejemplo a continuación:

root[test]> show create table sbtest2\G

*************************** 1. row ***************************

       Table: sbtest2

Create Table: CREATE TABLE `sbtest2` (

  `id` int(10) unsigned NOT NULL,

  `k` int(10) unsigned NOT NULL DEFAULT '0',

  `c` char(120) NOT NULL DEFAULT '',

  `pad` char(60) NOT NULL DEFAULT ''

) ENGINE=InnoDB DEFAULT CHARSET=latin1

1 row in set (0.00 sec)



root[test]> explain extended update sbtest2 set k=52, pad="xx234xh1jdkHdj234" where id=57;

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

| id | select_type | table   | partitions | type | possible_keys | key  | key_len | ref | rows | filtered | Extra       |

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

|  1 | UPDATE      | sbtest2 | NULL       | ALL | NULL | NULL | NULL    | NULL | 1923216 | 100.00 | Using where |

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

1 row in set, 1 warning (0.06 sec)

Mientras que una tabla con clave principal tiene un plan de consulta muy bueno,

root[test]> show create table sbtest3\G

*************************** 1. row ***************************

       Table: sbtest3

Create Table: CREATE TABLE `sbtest3` (

  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,

  `k` int(10) unsigned NOT NULL DEFAULT '0',

  `c` char(120) NOT NULL DEFAULT '',

  `pad` char(60) NOT NULL DEFAULT '',

  PRIMARY KEY (`id`),

  KEY `k` (`k`)

) ENGINE=InnoDB AUTO_INCREMENT=2097121 DEFAULT CHARSET=latin1

1 row in set (0.00 sec)



root[test]> explain extended update sbtest3 set k=52, pad="xx234xh1jdkHdj234" where id=57;

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

| id | select_type | table   | partitions | type | possible_keys | key     | key_len | ref | rows | filtered | Extra   |

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

|  1 | UPDATE      | sbtest3 | NULL       | range | PRIMARY | PRIMARY | 4       | const | 1 | 100.00 | Using where |

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

1 row in set, 1 warning (0.00 sec)

Las claves primarias o únicas proporcionan un componente vital para la estructura de una tabla porque esto es muy importante, especialmente cuando se realiza el mantenimiento de una tabla. Por ejemplo, el uso de herramientas del kit de herramientas de Percona (como pt-online-schema-change o pt-table-sync) recomienda que debe tener claves únicas. Tenga en cuenta que PRIMARY KEY ya es una clave única y una clave principal no puede contener valores NULL sino una clave única. Asignar un valor NULL a una clave principal puede causar un error como,

ERROR 1171 (42000): All parts of a PRIMARY KEY must be NOT NULL; if you need NULL in a key, use UNIQUE instead

Para los nodos esclavos, también es común que en ciertas ocasiones, la clave principal/única no esté presente en la tabla, lo que por lo tanto es una discrepancia en la estructura de la tabla. Puede usar mysqldiff para lograr esto o mysqldump --no-data … params y ejecutar una diferencia para comparar su estructura de tabla y verificar si hay alguna discrepancia.

Escanear tablas con índices duplicados y luego eliminarlos

Los índices duplicados también pueden causar una degradación del rendimiento, especialmente cuando la tabla contiene una gran cantidad de registros. MySQL tiene que realizar múltiples intentos para optimizar la consulta y realiza más planes de consulta para verificar. Incluye el análisis de grandes distribuciones de índices o estadísticas, lo que aumenta la sobrecarga de rendimiento, ya que puede causar contención de la memoria o una alta utilización de la memoria de E/S.

La degradación de las consultas cuando se observan índices duplicados en una tabla también se atribuye a la saturación del grupo de búfer. Esto también puede afectar el rendimiento de MySQL cuando los puntos de control vacían los registros de transacciones en el disco. Esto se debe al procesamiento y almacenamiento de un índice no deseado (que de hecho es una pérdida de espacio en el tablespace particular de esa tabla). Tenga en cuenta que los índices duplicados también se almacenan en el espacio de tabla, que también debe almacenarse en el grupo de búfer.

Eche un vistazo a la siguiente tabla que contiene varias claves duplicadas:

root[test]#> show create table sbtest3\G

*************************** 1. row ***************************

       Table: sbtest3

Create Table: CREATE TABLE `sbtest3` (

  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,

  `k` int(10) unsigned NOT NULL DEFAULT '0',

  `c` char(120) NOT NULL DEFAULT '',

  `pad` char(60) NOT NULL DEFAULT '',

  PRIMARY KEY (`id`),

  KEY `k` (`k`,`pad`,`c`),

  KEY `kcp2` (`id`,`k`,`c`,`pad`),

  KEY `kcp` (`k`,`c`,`pad`),

  KEY `pck` (`pad`,`c`,`id`,`k`)

) ENGINE=InnoDB AUTO_INCREMENT=2048561 DEFAULT CHARSET=latin1

1 row in set (0.00 sec)

y tiene un tamaño de 2.3GiB

root[test]#> \! du -hs /var/lib/mysql/test/sbtest3.ibd

2.3G    /var/lib/mysql/test/sbtest3.ibd

Eliminemos los índices duplicados y reconstruyamos la tabla con una alteración no operativa,

root[test]#> drop index kcp2 on sbtest3; drop index kcp on sbtest3 drop index pck on sbtest3;

Query OK, 0 rows affected (0.01 sec)

Records: 0  Duplicates: 0  Warnings: 0

Query OK, 0 rows affected (0.01 sec)

Records: 0  Duplicates: 0  Warnings: 0

Query OK, 0 rows affected (0.01 sec)

Records: 0  Duplicates: 0  Warnings: 0



root[test]#> alter table sbtest3 engine=innodb;

Query OK, 0 rows affected (28.23 sec)

Records: 0  Duplicates: 0  Warnings: 0



root[test]#> \! du -hs /var/lib/mysql/test/sbtest3.ibd

945M    /var/lib/mysql/test/sbtest3.ibd

Ha sido capaz de ahorrar hasta ~59 % del tamaño anterior del espacio de tabla, que es realmente enorme.

Para determinar índices duplicados, puede usar pt-duplicate-checker para manejar el trabajo por usted.

Ajuste su grupo de almacenamiento intermedio

Para esta sección me refiero únicamente al motor de almacenamiento InnoDB.

El grupo de búfer es un componente importante dentro del espacio del kernel de InnoDB. Aquí es donde InnoDB almacena en caché los datos de tablas e índices cuando se accede a ellos. Acelera el procesamiento porque los datos de uso frecuente se almacenan en la memoria de manera eficiente utilizando BTREE. Por ejemplo, si tiene varias tablas que consisten en>=100GiB y se accede mucho a ellas, le sugerimos que delegue una memoria volátil rápida a partir de un tamaño de 128GiB y comience a asignar el grupo de búfer con el 80 % de la memoria física. El 80% tiene que ser monitoreado de manera eficiente. Puede usar SHOW ENGINE INNODB STATUS \G o puede aprovechar el software de monitoreo como ClusterControl, que ofrece un monitoreo detallado que incluye el grupo de búfer y sus métricas de salud relevantes. Configure también la variable innodb_buffer_pool_instances en consecuencia. Puede configurarlo en un valor superior a 8 (predeterminado si innodb_buffer_pool_size>=1GiB), como 16, 24, 32 o 64 o superior si es necesario.

Al monitorear el grupo de búfer, debe verificar la variable de estado global Innodb_buffer_pool_pages_free, que le informa si es necesario ajustar el grupo de búfer, o tal vez considerar si también hay índices no deseados o duplicados que consumen el buffer. SHOW ENGINE INNODB STATUS \G también ofrece un aspecto más detallado de la información del grupo de búfer, incluido su grupo de búfer individual en función del número de innodb_buffer_pool_instances que haya establecido.

Usar índices de TEXTO COMPLETO (pero solo si corresponde)

Usando consultas como,

SELECT bookid, page, context FROM books WHERE context like '%for dummies%';

donde el contexto es una columna de tipo cadena (char, varchar, text), ¡es un ejemplo de una consulta súper mala! Extraer un gran contenido de registros con un filtro que tiene que ser codicioso termina con un escaneo completo de la tabla, y eso es una locura. Considere usar el índice FULLTEXT. Los índices A FULLTEXT tienen un diseño de índice invertido. Los índices invertidos almacenan una lista de palabras y, para cada palabra, una lista de documentos en los que aparece la palabra. Para admitir la búsqueda por proximidad, también se almacena la información de posición de cada palabra, como un desplazamiento de bytes.

Para usar FULLTEXT para buscar o filtrar datos, necesita usar la combinación de MATCH() ... CONTRA la sintaxis y no como la consulta anterior. Por supuesto, debe especificar el campo para que sea su campo de índice FULLTEXT.

Para crear un índice FULLTEXT, simplemente especifique FULLTEXT como su índice. Vea el siguiente ejemplo:

root[minime]#> CREATE FULLTEXT INDEX aboutme_fts ON users_info(aboutme);

Query OK, 0 rows affected, 1 warning (0.49 sec)

Records: 0  Duplicates: 0  Warnings: 1



root[jbmrcd_date]#> show warnings;

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

| Level   | Code | Message                                          |

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

| Warning |  124 | InnoDB rebuilding table to add column FTS_DOC_ID |

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

1 row in set (0.00 sec)

Aunque el uso de índices FULLTEXT puede ofrecer beneficios al buscar palabras dentro de un contexto muy grande dentro de una columna, también crea problemas cuando se usa incorrectamente.

Al realizar una búsqueda de TEXTO COMPLETO para una tabla grande a la que se accede constantemente (donde varias solicitudes de clientes buscan palabras clave diferentes y únicas), podría consumir mucho CPU.

También hay ciertas ocasiones en las que FULLTEXT no es aplicable. Consulte esta publicación de blog externa. Aunque no he probado esto con 8.0, no veo ningún cambio relevante para esto. Sugerimos que no use FULLTEXT para buscar en un entorno de big data, especialmente para tablas de alto tráfico. De lo contrario, intente aprovechar otras tecnologías como Apache Lucene, Apache Solr, tsearch2 o Sphinx.

Evite usar NULL en las columnas

Las columnas que contienen valores nulos están totalmente bien en MySQL. Pero si usa columnas con valores nulos en un índice, puede afectar el rendimiento de la consulta, ya que el optimizador no puede proporcionar el plan de consulta correcto debido a la mala distribución del índice. Sin embargo, hay ciertas formas de optimizar las consultas que involucran valores nulos, pero por supuesto, si esto se ajusta a los requisitos. Consulte la documentación de MySQL sobre la optimización nula. También puede consultar esta publicación externa que también es útil.

Diseñe su estructura de tabla y topología de esquema de manera eficiente

Hasta cierto punto, la normalización de las tablas de su base de datos de 1NF (primera forma normal) a 3NF (tercera forma normal) le brinda algunos beneficios para la eficiencia de las consultas porque las tablas normalizadas tienden a evitar registros redundantes. Una adecuada planificación y diseño de tus tablas es muy importante porque así es como recuperas o extraes datos y en cada una de estas acciones tiene un costo. Con las tablas normalizadas, el objetivo de la base de datos es garantizar que cada columna que no sea clave en cada tabla dependa directamente de la clave; toda la llave y nada más que la llave. Si se alcanza este objetivo, paga los beneficios en forma de redundancias reducidas, menos anomalías y eficiencias mejoradas.

Si bien la normalización de sus tablas tiene muchos beneficios, no significa que deba normalizar todas sus tablas de esta manera. Puede implementar un diseño para su base de datos utilizando Star Schema. Diseñar sus tablas usando Star Schema tiene el beneficio de consultas más simples (evite uniones cruzadas complejas), datos fáciles de recuperar para informes, ofrece ganancias de rendimiento porque no hay necesidad de usar uniones o uniones complejas o agregaciones rápidas. Un esquema en estrella es simple de implementar, pero debe planificarlo cuidadosamente porque puede crear grandes problemas y desventajas cuando su tabla crece y requiere mantenimiento. Star Schema (y sus tablas subyacentes) son propensos a problemas de integridad de datos, por lo que es muy probable que muchos de sus datos sean redundantes. Si cree que esta tabla tiene que ser constante (estructura y diseño) y está diseñada para utilizar la eficiencia de consulta, entonces es un caso ideal para este enfoque.

Mezclar los diseños de su base de datos (siempre y cuando pueda determinar e identificar qué tipo de datos deben extraerse de sus tablas) es muy importante ya que puede beneficiarse con consultas más eficientes y también ayudar al DBA con las copias de seguridad, el mantenimiento y la recuperación.

Deshágase de los datos antiguos y constantes

Recientemente escribimos algunas Mejores prácticas para archivar su base de datos en la nube. Cubre cómo puede aprovechar el archivo de datos antes de que vaya a la nube. Entonces, ¿cómo ayuda a mejorar la eficiencia de las consultas deshacerse de los datos antiguos o archivar sus datos constantes y antiguos? Como dije en mi blog anterior, existen beneficios para las tablas más grandes que se modifican constantemente y se insertan con nuevos datos, el espacio de tablas puede crecer rápidamente. MySQL e InnoDB funcionan de manera eficiente cuando los registros o los datos son contiguos y tienen importancia para su siguiente fila en la tabla. Es decir, si no tiene registros antiguos que ya no necesite usar, entonces el optimizador no necesita incluir eso en las estadísticas para ofrecer un resultado mucho más eficiente. Tiene sentido, ¿verdad? Y también, la eficiencia de las consultas no está solo en el lado de la aplicación, también debe considerar su eficiencia cuando se realiza una copia de seguridad y cuando se realiza un mantenimiento o una conmutación por error. Por ejemplo, si tiene una consulta incorrecta y larga que puede afectar su período de mantenimiento o una conmutación por error, eso puede ser un problema.

Habilitar el registro de consultas según sea necesario

Configure siempre el registro de consultas lentas de MySQL de acuerdo con sus necesidades personalizadas. Si está utilizando Percona Server, puede aprovechar su registro de consultas lentas extendidas. Le permite definir de forma habitual ciertas variables. Puede filtrar tipos de consultas en combinación, como full_scan, full_join, tmp_table, etc. También puede dictar la tasa de registro lento de consultas a través de la variable log_slow_rate_type y muchas otras.

La importancia de habilitar el registro de consultas en MySQL (como la consulta lenta) es beneficiosa para inspeccionar sus consultas para que pueda optimizar o ajustar su MySQL ajustando ciertas variables que se adapten a sus requisitos. Para habilitar el registro de consultas lentas, asegúrese de que estas variables estén configuradas:

  • long_query_time:asigne el valor correcto para el tiempo que pueden tardar las consultas. Si las consultas tardan más de 10 segundos (predeterminado), se reducirá al archivo de registro de consultas lentas que asignó.
  • slow_query_log:para habilitarlo, configúrelo en 1.
  • slow_query_log_file:esta es la ruta de destino para su archivo de registro de consultas lentas.

El registro de consultas lentas es muy útil para el análisis de consultas y el diagnóstico de consultas incorrectas que causan bloqueos, demoras de esclavos, consultas de ejecución prolongada, uso intensivo de memoria o CPU, o incluso que el servidor se bloquee. Si usa pt-query-digest o pt-index-usage, use el archivo de registro de consultas lentas como su destino de origen para informar estas consultas por igual.

Conclusión

Hemos discutido algunas formas que puede usar para maximizar la eficiencia de las consultas a la base de datos en este blog. En la siguiente parte, analizaremos aún más factores que pueden ayudarlo a maximizar el rendimiento. ¡Estén atentos!