Una simple consulta de Mysql

Recientemente encontré un problema "mágico" en mi trabajo, que puede ser útil para todos, por lo que este artículo está formado.

El problema es probablemente que tengo dos tablas, TableA y TableB, de las cuales TableA tiene aproximadamente un millón de filas (datos comerciales de acciones), y TableB tiene algunas filas (nuevos escenarios comerciales, los datos aún no se han expandido).

imagen

Semánticamente, TableA.columnA = TableB.columnA, donde un índice se construye en columnA, pero la consulta es realmente lenta, básicamente de 5 a 6 segundos, lo que obviamente no cumple con las expectativas.

Permítanme ilustrar con un ejemplo específico para simular el escenario de consulta SQL.

La escena reproduce la
tabla user_info. Para hacer la escena lo más simple posible, solo me burlé de tres columnas de datos. La tabla user_score, donde uid y user_info.uid tienen la misma semántica.

 

image.png

La situación de los datos es la siguiente, que son escenarios muy comunes:

 

image.png

La situación del índice es la siguiente:

 

image.png

Consultar escenarios de negocios: conociendo user_score.id, debe correlacionar y consultar la información correspondiente a user_info (ignoremos si este escenario de negocios específico es razonable o no).

Entonces, el SQL correspondiente es naturalmente el siguiente:

 

image.png

Por favor, ignore los datos, simplemente me burlé de 100W al principio y luego los importé dos veces, por lo que los datos son un poco duplicados.

300W de datos, la consulta final es de 1,18 segundos, debería ser más rápido por razón, ¿las reglas antiguas explican qué sucede?

 

image.png

¿Encontró que la tabla user_info no usa el índice y la tabla completa escanea casi 300W de datos? Este es el fenómeno, ¿por qué?

También podría pensar en ello, si se encuentra con un escenario así, ¿cómo debería solucionarlo?

image.png

En ese momento, estaba "operando tan ferozmente como un tigre" en ese momento. ¿Qué varios métodos de escritura SQL se han probado para realizar esta operación?

Por ejemplo, cambie el orden de la tabla de unión (tabla inicial / tabla conducida) o utilice una subconsulta. Al final, no hubo resultado. Pero la consulta directa de una sola tabla y la escritura SQL pueden usar índices.

 

image.png

El problema está solucionado,
intenta cambiar las condiciones de búsqueda, como cambiar el uid para asociar directamente la consulta, el índice aún no está disponible y casi se da por vencido.

 

 

Antes de prepararme para pedir ayuda al DBA, leí la declaración de creación de la tabla en la siguiente tabla:

image.png

Hay razones para sospechar que el índice no es válido debido a la inconsistencia del juego de caracteres.

Por lo tanto, modifique el conjunto de caracteres de la tabla pequeña (no se pierda en el entorno real en línea) para que sea coherente con la tabla grande y luego pruebe:

 

mysql> select * from user_score us
    -> inner join user_info ui on us.uid = ui.uid
    -> where us.id = 5;
+----+-----------+-------+---------+-----------+---------+
| id | uid       | score | id      | uid       | name    |
+----+-----------+-------+---------+-----------+---------+
|  5 | 111111111 |   100 |       1 | 111111111 | tanglei |
|  5 | 111111111 |   100 | 3685399 | 111111111 | tanglei |
|  5 | 111111111 |   100 | 3685400 | 111111111 | tanglei |
|  5 | 111111111 |   100 | 3685401 | 111111111 | tanglei |
|  5 | 111111111 |   100 | 3685402 | 111111111 | tanglei |
|  5 | 111111111 |   100 | 3685403 | 111111111 | tanglei |
+----+-----------+-------+---------+-----------+---------+
6 rows in set (0.00 sec)

mysql> explain
    -> select * from user_score us
    -> inner join user_info ui on us.uid = ui.uid
    -> where us.id = 5;
+----+-------------+-------+-------+-------------------+-----------+---------+-------+------+-------+
| id | select_type | table | type  | possible_keys     | key       | key_len | ref   | rows | Extra |
+----+-------------+-------+-------+-------------------+-----------+---------+-------+------+-------+
|  1 | SIMPLE      | us    | const | PRIMARY,index_uid | PRIMARY   | 4       | const |    1 | NULL  |
|  1 | SIMPLE      | ui    | ref   | index_uid         | index_uid | 194     | const |    6 | NULL  |
+----+-------------+-------+-------+-------------------+-----------+---------+-------+------+-------+
2 rows in set (0.00 sec)

Realmente funcionó.

Profundizando en la causa raíz
De hecho, la razón es que, como se menciona en varios reglamentos / estatutos militares de MySQL en Internet, "la columna de índice no debería participar en el cálculo".

En este caso, si conoce la herramienta "explicar extendido + mostrar advertencias" (no sabía que el parámetro extendido se puede agregar después de explicar antes), podría "entenderse de repente" tan pronto como sea posible. (La última versión de MySQL 8.0 no parece necesitar agregar esta palabra clave)

Mira el efecto: (Ah, tengo que cambiar el juego de caracteres)

 

mysql> explain extended select * from user_score us  inner join user_info ui on us.uid = ui.uid where us.id = 5;
+----+-------------+-------+-------+-------------------+---------+---------+-------+---------+----------+-------------+
| id | select_type | table | type  | possible_keys     | key     | key_len | ref   | rows    | filtered | Extra       |
+----+-------------+-------+-------+-------------------+---------+---------+-------+---------+----------+-------------+
|  1 | SIMPLE      | us    | const | PRIMARY,index_uid | PRIMARY | 4       | const |       1 |   100.00 | NULL        |
|  1 | SIMPLE      | ui    | ALL   | NULL              | NULL    | NULL    | NULL  | 2989934 |   100.00 | Using where |
+----+-------------+-------+-------+-------------------+---------+---------+-------+---------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)
mysql> show warnings;
+-------+------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Level | Code | Message                                                                                                                                                                                                                                                                              |
+-------+------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Note  | 1003 | /* select#1 */ select '5' AS `id`,'111111111' AS `uid`,'100' AS `score`,`test`.`ui`.`id` AS `id`,`test`.`ui`.`uid` AS `uid`,`test`.`ui`.`name` AS `name` from `test`.`user_score` `us` join `test`.`user_info` `ui` where (('111111111' = convert(`test`.`ui`.`uid` using utf8mb4))) |
+-------+------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

La columna de índice participa en el cálculo, cada vez que necesita convertirse de acuerdo con el conjunto de caracteres, y se escanea toda la tabla, ¿puede decir que puede ser más rápido?

¿En cuanto a por qué ocurre este problema? En general, es debido a razones históricas que la tabla original en el escenario de negocios antiguo es utf8 falso, y la nueva tabla de negocios usa utf8mb4 real.

① Al considerar la nueva tabla, ignore la comparación con el juego de caracteres de la biblioteca original. De hecho, descubrí que diferentes tablas en la biblioteca pueden tener diferentes conjuntos de caracteres, y diferentes personas pueden elegir diferentes conjuntos de caracteres según sus preferencias personales cuando las construyen. Esto muestra la importancia de las especificaciones de desarrollo.

②Aunque se sabe que las columnas de índice no pueden participar en el cálculo, todas son del mismo tipo en este escenario, y la conversión de tipo todavía ocurre durante la consulta final de varchar (64). Por lo tanto, es necesario equiparar la inconsistencia de los juegos de caracteres de campo con los tipos de campo inconsistentes.

③Si en este caso, utilizando el concepto de falla rápida, si se encuentran inconsistencias, ¿sería mejor no unirse? (Al igual que char vs varchar no se pueden unir)

Nota: El escenario de prueba de este artículo se basa en MySQL 5.6. Además, el caso de este artículo es solo para ilustrar el problema. El SQL que contiene no está estandarizado (por ejemplo, intente no usar select *, etc.), no lo imite (no soy responsable de imitarlo).

Finalmente, deje una pregunta de pensamiento para discusión, bienvenido a dejar un mensaje para expresar su opinión.

¿Puede explicar la siguiente situación? ¿Por qué los resultados de la consulta son inconsistentes? Preste atención al orden de ejecución de SQL, el flujo de trabajo del optimizador de consultas y el uso del búfer de unión (Block Nested Loop).

Puede echar un vistazo al manual oficial de MySQL para aprender más sobre el proceso y los principios detrás de él:

 

https://dev.mysql.com/doc/refman/5.6/en/

 

Supongo que te gusta

Origin blog.csdn.net/suifeng629/article/details/107428781
Recomendado
Clasificación