Artículos avanzados de MySQL: clasificación, agrupación y optimización de paginación

navegación:

[Notas de Java + Resumen de pisar el pozo] Conceptos básicos de Java + Avanzado + JavaWeb + SSM + SpringBoot + St. Regis Takeaway + SpringCloud + Dark Horse Tourism + Guli Mall + Xuecheng Online + Capítulo avanzado de MySQL + Modo de diseño + Preguntas de la entrevista de Nioke

Tabla de contenido

5. Optimización de clasificación

5.1 Sugerencias de optimización de clasificación

5.2 Pruebas

5.2.1 Verificación de caso

5.3.2 Ejercicios

5.3 Selección de campo de índice en consulta de rango

5.4 algoritmo de clasificación de archivos

5.4.1 Clasificación bidireccional y clasificación unidireccional

5.4.2 Ajuste del tipo de archivos

6. Optimización del grupo

7. Optimización de consultas de paginación

7.1 Optimización de consultas de paginación profunda

7.2 Optimización de paginación profunda con clasificación


5. Optimización de clasificación

5.1 Sugerencias de optimización de clasificación

Pregunta : Agregue un índice en el campo de condición DÓNDE, pero ¿por qué necesita agregar un índice en el campo ORDENAR POR ?

En MySQL, se admiten dos métodos de clasificación, a saber, FileSort y clasificación por índice. 

  • Clasificación de índice: en la clasificación de índice, el índice puede garantizar el orden de los datos, no es necesario ordenarlos y es más eficiente, se recomienda utilizarlo .
  • Clasificación FileSort: la clasificación FileSort generalmente se realiza en la memoria , lo que ocupa más CPU . Si el resultado a ordenar es grande, se producirá una E/S de archivos temporales al disco para ordenar, lo cual es ineficiente.

Sugerencias de optimización:

  • El optimizador selecciona automáticamente el método de clasificación: MySQL admite la clasificación por índice y la clasificación FileSort. El índice garantiza el orden de los registros y tiene un alto rendimiento. Se recomienda su uso. La clasificación FileSort es una clasificación en memoria. Cuando la cantidad de datos es grande, se generan y clasifican archivos temporales en el disco, la eficiencia es baja y consume mucha CPU. No es que FileSort sea necesariamente ineficiente, puede ser eficiente en algunos casos. Por ejemplo, para consultas difusas a la izquierda y "no iguales a" que no cubren el índice, la eficiencia del escaneo completo de la tabla es mayor que la del recorrido del índice y luego el regreso a la tabla.
  • Para cumplir con el prefijo más a la izquierda: cuando las condiciones y el orden por campos crean un índice conjunto, el orden debe cumplir con el prefijo más a la izquierda. Por ejemplo, índice (a,b,c), consulta donde a=1 ordena por b,c.
  • El índice de clasificación en el lado derecho de la consulta de rango no es válido: por ejemplo, índice (a,b,c), consulta donde a>1 ordena por b,c, lo que da como resultado que la clasificación b,c no pueda usar el índice y ordenar archivos se requiere.
  • Todo ascendente o todo descendente: el orden de clasificación debe ser todo DESC o todo ASC. Si no funciona, el índice fallará.
  • Cuando el número a ordenar es grande, el índice dejará de ser válido: la cantidad de datos a ordenar excede aproximadamente 10,000, por lo que no se usará el índice y se usará la clasificación de archivos. Se recomienda utilizar límite y dónde filtrar para reducir la cantidad de datos. Cuando la cantidad de datos es grande, es necesario volver a la tabla para verificar todos los datos después de ordenar el índice, y el rendimiento es muy pobre y no es tan eficiente como FileSort para ordenar en la memoria. No significa que el uso del límite definitivamente usará la clasificación por índice. La clave es la cantidad de datos. Cuando la cantidad de datos es demasiado grande, el optimizador usará FileSort para ordenar.
  • Agregar índices a los campos de rango de prioridad: cuando los campos [condición de rango] y [agrupar por u ordenar por] parecen ser opcionales, si hay suficientes datos filtrados pero no muchos datos para ordenar, el índice se coloca primero en el campo de rango . De esta manera, incluso si la consulta de rango hace que falle el índice de clasificación, la eficiencia sigue siendo mayor que cuando solo se indexa el campo de clasificación. Si solo puede filtrar un poco, primero coloque el índice en el campo de clasificación.
  • Ajuste de FileSort: cuando no se puede utilizar la clasificación por índice, es necesario ajustar el método FileSort. Por ejemplo, aumente sort_buffer_size (tamaño del búfer de clasificación) y max_length_for_sort_data (longitud máxima de los datos ordenados)

5.2 Pruebas

5.2.1 Verificación de caso

Elimine los índices creados en la tabla de estudiantes y en la tabla de clases.

# 方式1
DROP INDEX idx_monitor ON class;
DROP INDEX idx_cid ON student;
DROP INDEX idx_age ON student;
DROP INDEX idx_name ON student;
DROP INDEX idx_age_name_classId ON student;
DROP INDEX idx_age_classId_name ON student;

# 方式2:call调用删除函数
call proc_drop_index('atguigudb2','student');

¿Se puede utilizar el siguiente índice y se puede eliminar el tipo de archivo de uso?

Vaya directamente a ordenar archivos sin indexar:

#索引失败。没有limit
EXPLAIN SELECT SQL_NO_CACHE * FROM student ORDER BY age,classid;

  

  

Después de agregar el índice, si el orden no está limitado, la cantidad de datos será demasiado grande y el índice dejará de ser válido:

CREATE INDEX idx_age_classid_name ON student(age,classId,name);
#索引失败。没有limit
EXPLAIN SELECT SQL_NO_CACHE * FROM student ORDER BY age,classid;

#索引成功,key_len为73
EXPLAIN SELECT SQL_NO_CACHE * FROM student ORDER BY age,classid LIMIT 10000;

Proceso 3: el orden es incorrecto cuando se ordena por y el índice no es válido

#创建索引age,classid,stuno
#call proc_drop_index('atguigudb2','student');
CREATE INDEX idx_age_classid_stuno ON student (age,classid,stuno);
#索引失效,不符合最左前缀
EXPLAIN SELECT * FROM student ORDER BY classid LIMIT 10;
#索引失效,不符合最左前缀
EXPLAIN SELECT * FROM student ORDER BY classid,name LIMIT 10;
#索引失效,不符合最左前缀
EXPLAIN SELECT * FROM student WHERE classid=1 ORDER BY age,stuno;
#全走索引,虽然不符合最左前缀,但因为查询量小,优化器先排序三个字段,再where找10个返回。
#优化器认为索引比filesort效率高,就用了索引
EXPLAIN SELECT * FROM student WHERE classid=1 ORDER BY age,stuno LIMIT 10;
#索引成功,符合最左前缀
EXPLAIN SELECT * FROM student ORDER BY age,classid,stuno LIMIT 10;
#索引成功,符合最左前缀
EXPLAIN SELECT * FROM student ORDER BY age,classid LIMIT 10;

Proceso 4: las reglas son inconsistentes cuando se ordena por y el índice falla (el orden es incorrecto, no hay indexación; la dirección está invertida, no hay indexación)

Debe coincidir con el prefijo más a la izquierda y "todo ascendente o todo descendente"

#创建索引age,classid,stuno
CREATE INDEX idx_age_classid_stuno ON student (age,classid,stuno);
#没符合“全升序或全降序”,索引失效
EXPLAIN SELECT * FROM student ORDER BY age DESC,classid ASC LIMIT 10;
#没符合最左前缀,索引失效
EXPLAIN SELECT * FROM student ORDER BY classid DESC,name DESC LIMIT 10;
#没符合“全升序或全降序”,索引失效
EXPLAIN SELECT * FROM student ORDER BY age ASC,classid DESC LIMIT 10;
#符合最左前缀,符合“全升序或全降序”,索引成功
EXPLAIN SELECT * FROM student ORDER BY age DESC,classid DESC LIMIT 10;

Proceso 5: Si la cantidad de datos límite es pequeña, es posible ir al índice si no se cumple el prefijo más a la izquierda, primero ordenar y luego filtrar dónde.

CREATE INDEX idx_age_classid_stuno ON student (age,classid,stuno);
CREATE INDEX idx_age_classid_name ON student(age,classId,name);

#都走了索引。
EXPLAIN SELECT * FROM student WHERE age=45 ORDER BY classid LIMIT 10;
#都走了索引。
EXPLAIN SELECT * FROM student WHERE age=45 ORDER BY classid,name;
#都没用索引,不符合最左前缀
EXPLAIN SELECT * FROM student WHERE classid=45 order by age;
#全走了索引。因为limit数据量小,优化器直接先用排序字段索引排序,然后再where筛选10个
EXPLAIN SELECT * FROM student WHERE classid=45 order by age limit 10;

La búsqueda de rango hace que el índice falle: hay índices (userDbid, AddressDbid, createTime) debajo, userDbid, AddressDbid van al índice, porque AddressDbid es una búsqueda de rango, lo que hace que el índice CreateTime falle.

 

 

5.3.2  Ejercicios

INDEX a_b_c(a,b,c)
order by 能使用索引最左前缀
- ORDER BY a
- ORDER BY a,b
- ORDER BY a,b,c
- ORDER BY a DESC,b DESC,c DESC
如果WHERE使用索引的最左前缀定义为常量,则order by 能使用索引
- WHERE a = const ORDER BY b,c
- WHERE a = const AND b = const ORDER BY c
- WHERE a = const ORDER BY b,c
- WHERE a = const AND b > const ORDER BY b,c
不能使用索引进行排序
- ORDER BY a ASC,b DESC,c DESC /* 排序不一致 */
- WHERE g = const ORDER BY b,c /*丢失a索引*/
- WHERE a = const ORDER BY c /*丢失b索引*/
- WHERE a = const ORDER BY a,d /*d不是索引的一部分*/
- WHERE a in (...) ORDER BY b,c /*对于排序来说,多个相等条件也是范围查询*/


 

5.3 Selección de campo de índice en consulta de rango

  1. mysql selecciona automáticamente la solución óptima: si existen dos índices al mismo tiempo, mysql selecciona automáticamente la solución óptima. (Para este ejemplo, mysql elige idx_age_stuno_name). Sin embargo, a medida que cambia la cantidad de datos, el índice seleccionado también cambiará en consecuencia.
  2. Cuando la proporción de filtrado es alta, primero agregue indexación a los campos de filtro: cuando los campos de [condición de rango] y [agrupar por u ordenar por] parecen elegir uno u otro, primero observe la cantidad de filtros en el campo de condición. hay suficientes datos filtrados, los datos que deben ordenarse cuando no hay muchos, es preferible colocar el índice en el campo de rango. viceversa.

Para la cláusula ORDER BY, intente utilizar el método Index para ordenar y evite utilizar el método FileSort para ordenar.

Antes de ejecutar el caso, borre el índice del estudiante, dejando solo la clave principal:

DROP INDEX idx_age ON student;
DROP INDEX idx_age_classid_stuno ON student;
DROP INDEX idx_age_classid_name ON student;

#或者
call proc_drop_index('atguigudb2','student');

Escenario: consultar estudiantes cuya edad es 30 y cuyo número de estudiante es inferior a 101000, ordenados por nombre de usuario

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age = 30 AND stuno <101000 ORDER BY NAME ;

Los resultados de la consulta son los siguientes:

mysql> SELECT SQL_NO_CACHE * FROM student WHERE age = 30 AND stuno <101000 ORDER BY NAME;
+---------+--------+--------+------+---------+
| id      | stuno  |  name  | age  | classId |
+---------+--------+--------+------+---------+
| 922     | 100923 | elTLXD | 30   | 249     |
| 3723263 | 100412 | hKcjLb | 30   | 59      |
| 3724152 | 100827 | iHLJmh | 30   | 387     |
| 3724030 | 100776 | LgxWoD | 30   | 253     |
| 30      | 100031 | LZMOIa | 30   | 97      |
| 3722887 | 100237 | QzbJdx | 30   | 440     |
| 609     | 100610 | vbRimN | 30   | 481     |
| 139     | 100140 | ZqFbuR | 30   | 351     |
+---------+--------+--------+------+---------+
8 rows in set, 1 warning (3.16 sec)

Conclusión: el tipo es TODO, el peor de los casos. El uso de clasificación de archivos también apareció en Extra, que también es el peor de los casos. La optimización es imprescindible.

Solución 1: para eliminar la clasificación de archivos, creamos un índice y la eficiencia de la consulta es un poco mayor

#创建新索引
CREATE INDEX idx_age_name ON student(age,NAME);
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age = 30 AND stuno <101000 ORDER BY NAME;

Sólo la edad ha pasado al índice: 

De esta manera optimizamos usando filesort

Los resultados de la consulta son los siguientes:

Solución 2: intente utilizar el índice superior para las condiciones de filtrado y la clasificación de dónde, y descubra que la eficiencia de la consulta es mayor

Cree un índice combinado de tres campos y descubra que el uso de clasificación de archivos todavía existe:

DROP INDEX idx_age_name ON student;
CREATE INDEX idx_age_stuno_name ON student (age,stuno,NAME);
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age = 30 AND stuno <101000 ORDER BY NAME;

age y stuno abandonaron el índice: 

Descubrimos que el uso de clasificación de archivos todavía existe, por lo que el nombre no usa el índice y el tipo sigue siendo rango, solo mirar el nombre en realidad no es bueno. La razón es que debido a que stuno es un filtro de rango , los campos detrás del índice ya no usarán el índice.

El resultado es el siguiente:

mysql> SELECT SQL_NO_CACHE * FROM student
-> WHERE age = 30 AND stuno <101000 ORDER BY NAME ;
+-----+--------+--------+------+---------+
| id | stuno | name | age | classId |
+-----+--------+--------+------+---------+
| 167 | 100168 | AClxEF | 30 | 319 |
| 323 | 100324 | bwbTpQ | 30 | 654 |
| 651 | 100652 | DRwIac | 30 | 997 |
| 517 | 100518 | HNSYqJ | 30 | 256 |
| 344 | 100345 | JuepiX | 30 | 329 |
| 905 | 100906 | JuWALd | 30 | 892 |
| 574 | 100575 | kbyqjX | 30 | 260 |
| 703 | 100704 | KJbprS | 30 | 594 |
| 723 | 100724 | OTdJkY | 30 | 236 |
| 656 | 100657 | Pfgqmj | 30 | 600 |
| 982 | 100983 | qywLqw | 30 | 837 |
| 468 | 100469 | sLEKQW | 30 | 346 |
| 988 | 100989 | UBYqJl | 30 | 457 |
| 173 | 100174 | UltkTN | 30 | 830 |
| 332 | 100333 | YjWiZw | 30 | 824 |
+-----+--------+--------+------+---------+
15 rows in set, 1 warning (0.00 sec)

El resultado fue que la clasificación de archivos se ejecutó más rápido que el índice, y mucho más rápido, y los resultados aparecieron casi instantáneamente.

razón:

Toda la clasificación se realiza después del filtrado condicional. Por lo tanto, si las condiciones filtran la mayoría de los datos, los cientos o miles de datos restantes para ordenar no requieren mucho rendimiento. Incluso si el índice optimiza la clasificación, la mejora real del rendimiento es muy limitada. En relación con la condición de stuno <101000, si no se utiliza ningún índice, se deben escanear decenas de miles de datos, lo que consume mucho rendimiento, por lo que colocar un índice en este campo es lo más rentable y lo mejor. elección

en conclusión:

  1. Existen dos índices al mismo tiempo, mysql elige automáticamente la solución óptima . (Para este ejemplo, mysql elige idx_age_stuno_name). Sin embargo, a medida que cambia la cantidad de datos, el índice seleccionado también cambiará en consecuencia .
  2. Cuando aparecen los campos de [condición de rango] y [agrupar por u ordenar por], primero se debe observar la cantidad de filtros en el campo de condición. Si hay suficientes datos filtrados y no hay muchos datos para ordenar, el índice debe colocarse primero en el campo rango. viceversa.

Pensando: Aquí usamos el siguiente índice, ¿es factible?

DROP INDEX idx_age_stuno_name ON student;

CREATE INDEX idx_age_stuno ON student(age,stuno);

seguro.

5.4 algoritmo de clasificación de archivos

5.4.1 Clasificación bidireccional y clasificación unidireccional

Si el campo ordenado no está en la columna de índice, la clasificación de archivos tendrá dos algoritmos: clasificación bidireccional y clasificación unidireccional.

Clasificación bidireccional (lenta)

  • Antes de MySQL 4.1 , se usaba la clasificación bidireccional, lo que literalmente significa escanear el disco dos veces para finalmente obtener los datos, leer el puntero de fila y ordenarlos por columna, ordenarlos, luego escanear la lista ordenada y comenzar desde la lista de acuerdo con el valores en la lista Lea la salida de datos correspondiente en la lista
  • Obtenga el campo de clasificación del disco, ordénelo en el búfer y luego obtenga otros campos del disco.

Para recuperar un lote de datos, es necesario escanear el disco dos veces. Como todos sabemos, IO requiere mucho tiempo, por lo que después de mysql4.1, apareció un segundo algoritmo mejorado, que es la clasificación unidireccional.

Clasificación unidireccional (rápida)

Lea todas las columnas requeridas por la consulta desde el disco, ordénelas en el búfer según el orden por columna y luego escanee la lista ordenada para obtener salida, lo que es más eficiente y evita leer datos por segunda vez. Y convierta IO aleatoria en IO secuencial, pero utilizará más espacio porque mantiene cada fila en la memoria.

Conclusión y preguntas planteadas.

  • Dado que el carril único sale al final, generalmente es mejor que el carril doble.

  • Pero hay un problema con el canal único.

    • En sort_buffer, el unidireccional ocupa mucho más espacio que el multidireccional, porque el unidireccional elimina todos los campos, por lo que es posible que el tamaño total de los datos extraídos exceda la capacidad de sort_buffer, por lo que solo los datos del tamaño de sort_buffer se puede recuperar cada vez , para ordenar (crear archivos tmp, fusión multidireccional), después de ordenar, tomar el tamaño de la capacidad de sort_buffer y luego ordenar... por lo tanto, múltiples E/S.
    • Originalmente, el canal único quería guardar una operación de E/S, pero resultó en una gran cantidad de operaciones de E/S y la ganancia superó la pérdida.

5.4.2 Ajuste del tipo de archivos

1. Intente aumentar sort_buffer_size

2. Intente aumentar max_length_for_sort_data

SHOW VARIABLES LIKE '%max_length_for_sort_data%';
#默认1924字节

Aumentar este parámetro aumentará la probabilidad de utilizar el algoritmo mejorado. Pero si se establece demasiado alto, aumentará la probabilidad de que la capacidad total de datos exceda el tamaño del búfer de clasificación. Los síntomas obvios son una alta actividad de E/S del disco y un bajo uso del procesador. Si la longitud total de las columnas a devolver es mayor que max_length_for_sort datos, use un algoritmo bidireccional, de lo contrario use un algoritmo unidireccional. Ajuste entre 1024-8192 bytes

3. Al realizar el pedido, seleccione * es un tabú. Lo mejor es consultar sólo los campos obligatorios.

  • Cuando la suma de los tamaños de los campos de Consulta es menor que max_ength_for_sort_data y el campo de clasificación no es del tipo TEXTBLOB, se utilizará el algoritmo mejorado: clasificación unidireccional; de lo contrario, se utilizará el algoritmo antiguo: clasificación multidireccional.
  • Los datos de ambos algoritmos pueden exceder la capacidad de sort_bufer_size. Después de excederla, se crearán archivos tmp para la clasificación por combinación, lo que dará como resultado múltiples I/0. Sin embargo, el riesgo de utilizar un algoritmo de clasificación unidireccional será mayor, por lo que sort_bufer_size debería ser incrementado.

6. Optimización del grupo

  • Similar a la optimización de clasificación: el principio de usar índices para agrupar por es casi el mismo que el de ordenar por, y agrupar por puede usar índices directamente incluso si no usa índices para las condiciones de filtrado.
  • Prefijo más a la izquierda: primero agrupe por clasificación y luego por grupos, siguiendo la regla del mejor prefijo izquierdo para la creación de índices.
  • Ajuste de FileSort : cuando no se pueden utilizar las columnas de índice, aumente la configuración de los parámetros max_length_for_sort_data y sort_buffer_size
  • donde es más eficiente que tener , y las condiciones que se pueden escribir en donde son limitadas no deben escribirse en tener. Donde está filtrar antes de agrupar y tener es filtrar después de agrupar.
  • Intente no ordenar grupos y ahorre CPU: reduzca el uso de ordenar y comuníquese con las empresas sin clasificar, o coloque la clasificación en el terminal. Declaraciones como Ordenar por, agrupar por y distinguir consumen más CPU y los recursos de CPU de la base de datos son extremadamente valiosos.
  • Límite de uso: declaraciones que incluyen consultas como ordenar por, agrupar por y distinguir. Mantenga el conjunto de resultados filtrado por la condición dónde dentro de 1000 filas; de lo contrario, el SQL será muy lento.

7. Optimización de consultas de paginación

7.1 Optimización de consultas de paginación profunda

En una consulta de paginación general, el rendimiento se puede mejorar creando un índice de cobertura .

Problema actual: cuando el desplazamiento es muy grande, es necesario consultar y luego paginar una gran cantidad de datos inútiles, lo que da como resultado un rendimiento deficiente.

Un problema común y muy problemático es el límite 2000000,10. En este momento, MySQL necesita ordenar los primeros 200000010 registros, solo devuelve 2000000 ~ 2000010 registros, otros registros se descartan y el costo de la clasificación de consultas es muy alto. Y seleccione * necesita volver a la tabla, lo que lleva más tiempo.

EXPLAIN SELECT * FROM student LIMIT 2000000,10; 

 Tabla con incremento automático de clave principal: verifique directamente los 10 datos después del rango. Una consulta de límite se puede convertir en una consulta de ubicación.

EXPLAIN SELECT * FROM student WHERE id > 2000000 LIMIT 10;

 Tablas con claves primarias no incrementales: la tabla de claves primarias después de la intercepción de clasificación de conexiones en la tabla actual , y el campo de conexión es la clave primaria.

EXPLAIN SELECT * FROM student t,(SELECT id FROM student ORDER BY id LIMIT 2000000,10) a WHERE t.id = a.id;

También puede utilizar subconsultas, que se optimizan en consultas asociadas.

7.2 Optimización de paginación profunda con clasificación

Antes de la optimización:  consulta de paginación profunda organizada en orden inverso según la edad

EXPLAIN SELECT * FROM student order by age desc LIMIT 2000000,10; 

Esquema de optimización 1:  la idea de optimización es la misma que antes, el campo de conexión interno es id

EXPLAIN SELECT * FROM student t1,(SELECT id FROM student ORDER BY age desc LIMIT 2000000,10) t2 WHERE t1.id=t2.id

Esquema de optimización 2: si pasa las páginas secuencialmente, puede obtener el último registro x de la página anterior, entonces todos los ID de registro del número de página de destino son menores que x.id (porque el orden inverso y la base de clasificación es en realidad la edad). , id), el número de página de destino Todos los registros con una antigüedad menor o igual a x.age.

EXPLAIN SELECT * FROM student WHERE id<#{x.id} AND age>=#{x.age} ORDER BY age DESC LIMIT 10;

Supongo que te gusta

Origin blog.csdn.net/qq_40991313/article/details/130790354
Recomendado
Clasificación