¿Cómo implementan las empresas de Internet la paginación y usan MySQL para limitar?

Al navegar por el sitio web, a menudo nos encontramos con escenarios que requieren una consulta de paginación.

Fácilmente podemos pensar que se puede lograr con mysql.

Supongamos que nuestra tabla sql es así

tabla mysql sql

No necesita deducir detalles al crear una tabla sql, solo necesita saber que  id es la clave principal, y es  suficiente para crear un índice de clave no principal en nombre_de_usuario, y el resto no es importante.

Para implementar la paginación.

Es fácil pensar en la siguiente instrucción sql.

seleccione * del orden de la página por desplazamiento de límite de identificación, tamaño;

Por ejemplo, hay 10 datos en una página.

Estado original de la base de datos de la tabla de usuario

La primera página es la siguiente instrucción sql.

seleccione * del orden de la página por límite de identificación 0, 10;

La centésima página es

seleccione * del orden de la página por límite de identificación 990, 10;

Así que aquí viene la pregunta.

De esta manera, también obtenemos 10 piezas de datos.¿La velocidad de consulta de la primera página y la centésima página es la misma? ¿Por qué?

El proceso de ejecución de los dos límites.

Los dos métodos de consulta anteriores. Correspondencia  limit offset, size y  limit size dos caminos.

De hecho  limit size , es equivalente a    limit 0, size . Eso es comenzar desde 0 para tomar datos de tamaño.

Es decir, la diferencia entre los dos métodos  es si el desplazamiento es 0.

Primero veamos la lógica de ejecución interna de limit sql.

esquema mysql

MySQL se divide internamente en  una capa de servidor  y  una capa de motor de almacenamiento  . En general, el motor de almacenamiento usa innodb.

Hay muchos módulos en la capa del servidor, entre los cuales el  ejecutor  es el componente que se utiliza para tratar con el motor de almacenamiento.

El ejecutor puede obtener datos fila por fila llamando a la interfaz proporcionada por el motor de almacenamiento. Cuando los datos cumplan completamente con los requisitos (como cumplir con otras condiciones), se colocarán  en el  conjunto  de resultados y finalmente se devolverán al cliente que llama . mysql (go, aplicación escrita en java)  .

Podemos ejecutar el siguiente sql primero  explain .

explique select * from page order by id limit 0, 10;

Como puede ver, donde se solicita la clave en la explicación,  se ejecuta PRIMARY  , que es el  índice de la clave principal  .

Desplazamiento de consulta de paginación = 0

El índice de clave principal es esencialmente un árbol B+, que es una estructura de datos ubicada en innodb.

Podemos recordar que el árbol B+ se ve así.

estructura de árbol B+

En esta estructura de árbol, debemos prestar atención al nodo en la capa inferior, es decir, el  nodo hoja  . La información en este nodo hoja variará según si el índice actual es una  clave principal o una clave no principal  .

  • Si es un  índice de clave principal  , sus nodos de hoja almacenarán información de datos de fila completa.

  • Si no es un índice de clave principal  , sus nodos de hoja almacenarán la clave principal. Si desea obtener información de datos de fila, debe ir al índice de clave principal para obtener los datos nuevamente, lo que se denomina  tabla de retorno  .

como ejecutar

seleccione * de la página donde nombre_usuario = "小白10";

 Los datos cuyo nombre de usuario es "  Xiaobai 10 " se consultarán a través del índice de clave no principal  , y luego  la clave principal correspondiente a los datos de  " Xiaobai 10 " se  encontrará en el nodo hoja  como 10  .

En este momento, devuelva la tabla al  índice de clave principal  para la consulta y, finalmente, ubique  los datos de la fila cuya clave principal es 10  .

formulario de devolución

Pero ya sea una clave principal o un índice de clave no principal, sus datos de nodo de hoja están  ordenados  . Por ejemplo, en el índice de la clave principal, los datos se ordenan según el tamaño de la identificación de la clave principal, de menor a mayor.

Limite el proceso de ejecución según el índice de clave principal

Volvamos a la pregunta del principio del artículo.

Cuando eliminemos la explicación, ejecute este sql.

seleccione * del orden de la página por límite de identificación 0, 10;

La selección anterior va seguida de un  asterisco *, es decir, se requiere obtener toda la información de campo de los datos  de la fila  .

La capa del servidor llamará a la interfaz de innodb, obtendrá los  datos de fila completos de 0 a 10 en el índice de clave principal en innodb,  los devolverá a la capa del servidor, lo colocará en el conjunto de resultados de la capa del servidor y lo devolverá. al cliente.

Y cuando hacemos que la compensación sea escandalosa, por ejemplo, la ejecución es

seleccione * del orden de la página por límite de identificación 6000000, 10;

La capa del servidor llamará a la interfaz innodb. Dado que el desplazamiento = 6000000 esta vez, las filas completas de datos 0 a (6000000 + 10)  se obtendrán del índice de clave principal en innodb,  y luego se devolverán a la capa del servidor y se descartará una por uno de acuerdo con el valor de compensación. , y finalmente solo la última barra de tamaño, es decir, 10 piezas de datos, se coloca en el conjunto de resultados de la capa del servidor y se devuelve al cliente.

Se puede ver que cuando el desplazamiento no es 0, la capa del servidor obtendrá una  gran cantidad de datos inútiles de la capa del motor  , y los datos inútiles obtenidos llevarán tiempo.

Por lo tanto, sabemos la respuesta a la pregunta al comienzo del artículo, el límite 1000,10 será más lento que el límite 10 en la consulta mysql. La razón es que el límite 1000,10 sacará 1000+10 piezas de datos y descartará las primeras 1000 piezas, lo que lleva más tiempo.

¿Hay alguna forma de optimizar este caso?

Se puede ver que cuando el desplazamiento no es 0, la capa del servidor obtendrá una gran cantidad de datos inútiles de la capa del motor, y cuando la selección va seguida de un *, necesita copiar la información de la fila completa,  copiar los datos completos  y  solo copie los datos de la fila. Los campos de una o dos columnas  toman tiempos diferentes, lo que hace que la operación que ya consume mucho tiempo sea aún más escandalosa.

Debido a que los datos de compensación anteriores no son necesarios al final, incluso si se copian los campos completos, cuál es el uso, entonces podemos modificar la declaración sql de la siguiente manera.

seleccionar * de la página donde id >=(seleccionar id de página ordenar por límite de id 6000000, 1) ordenar por límite de id 10;

En la instrucción sql anterior, la subconsulta se ejecuta primero  select id from page order by id limit 6000000, 1 . De hecho, esta operación también obtendrá piezas de datos en el índice de clave principal en innodb  6000000+1 , y luego la capa del servidor descartará las primeras 6,000,000 piezas, y solo conservará la identificación de la último dato.

Pero la diferencia es que en el proceso de volver a la capa del servidor, solo se copia la columna de identificación en la fila de datos, no todas las columnas de la fila de datos.Cuando la cantidad de datos es grande, el tiempo de esta parte es relativamente obvio. .

Después de obtener la identificación anterior, asumiendo que esta identificación es exactamente igual a 6000000, entonces el sql se convierte en

seleccione * de la página donde id> = (6000000) ordenar por límite de id 10;

De esta manera, innodb  vuelve a pasar por el índice de clave principal  , localiza rápidamente los datos de la fila con id=6000000 a través del árbol B+, la complejidad de tiempo es lg(n) y luego recupera 10 piezas de datos hacia atrás.

De esta manera, se mejora el rendimiento y la prueba personal puede ser aproximadamente el doble de rápida, lo que pertenece al tipo de operación que lleva un tiempo de 3 s a 1,5 s.

Este······

Es verdad, es una gota en el océano, un poco de frotamiento, y es una salida de la nada.

Limite el proceso de ejecución en función del índice de clave no principal

Lo mencionado anteriormente es el proceso de ejecución del índice de clave principal, veamos el   proceso de ejecución límite basado en el índice de clave no principal .

Por ejemplo, la siguiente instrucción sql

seleccione * del orden de la página por límite de nombre de usuario 0, 10;

La capa del servidor llamará a la interfaz de innodb.Después de obtener la identificación de clave principal correspondiente a los datos 0 en el índice de clave no principal en innodb,  regrese la tabla  al índice de clave principal para encontrar los datos de fila completos correspondientes, y luego regrese Se coloca en el conjunto de resultados y se devuelve al cliente.

Cuando offset>0, y el valor de offset es pequeño, la lógica es similar, la diferencia es que cuando offset>0, los datos de compensación anteriores se descartarán.

Es decir, el proceso de límite de un índice de clave no principal consume más tablas de retorno que el proceso de límite de un índice de clave principal.

Pero cuando la compensación se vuelve muy grande, como 6 millones, se ejecuta la explicación en este momento.

Realice un escaneo completo de la tabla cuando el valor de desplazamiento del índice de la clave no principal sea demasiado grande

Puede ver que la columna de tipo muestra TODO, que es  una exploración completa de la tabla  .

Esto se debe a que el optimizador en la capa del servidor   determinará qué plan de ejecución es menos costoso antes de que el ejecutor ejecute la instrucción SQL.

Obviamente, el optimizador negó con la cabeza después de ver los tiempos de 600w de volver a la mesa para el índice de clave no primaria.

Por lo tanto, cuando el desplazamiento del límite es demasiado grande, las consultas de índice de clave no principal pueden convertirse fácilmente en exploraciones de tabla completa. Un verdadero asesino del rendimiento.

Esta situación también se puede optimizar de alguna manera. por ejemplo

seleccione * de la página t1, (seleccione id del orden de la página por límite de nombre de usuario 6000000, 100) t2 DONDE t1.id = t2.id;

pasar  select id from page order by user_name limit 6000000, 100 _ Primero vaya al índice de clave no principal de nombre de usuario de la capa innodb para obtener la identificación, porque solo se usa la identificación de la clave principal y  no es necesario volver a la tabla  , por lo que el rendimiento de esta pieza será un poco más rápido. Después de regresar a la capa del servidor, los primeros 600w de datos también se descartan y se conservan los últimos 100. id. Luego use estos 100 ID para hacer coincidir el ID de la tabla t1. En este momento, se usa el índice de clave principal y se devuelven las 100 filas de datos coincidentes. Esto pasa por alto el formulario de devolución de los datos anteriores de 600w.

Por supuesto, como en el caso anterior, todavía no hay una solución al problema de tomar datos de 600w por nada y luego descartarlos, lo que también es una optimización muy frustrante.

Así, cuando la compensación se vuelve muy grande, como del orden de millones de millones, el problema de repente se vuelve serio.

Aquí viene un término especial llamado  paginación profunda  .

Problema de paginación profunda

El problema de la paginación profunda es un problema muy repugnante. Lo repugnante es repugnante. Este problema en realidad no tiene  solución  .

Ya sea que use mysql o es, solo puede "mitigar" la gravedad del problema de alguna manera.

Cuando nos encontramos con este problema, debemos mirar hacia atrás y pensar en ello.

¿Por qué nuestro código tiene problemas profundos de paginación?

¿Cuál es el requisito original detrás de esto? Podemos hacer algo de evasión en base a esto.

Si desea obtener los datos de toda la tabla

Algunos requisitos son como este, tenemos una tabla de base de datos, pero queremos sacar todos los datos en esta tabla de base de datos, heterogéneamente en es o hive. En este momento, si ejecutamos directamente

seleccione * de la página;

Tan pronto como se ejecutó este sql, el perro sacudió la cabeza cuando lo vio.

Debido a la gran cantidad de datos, mysql no puede obtener todos los datos a la vez, e  informará un error cuando se realicen las horas extra correctamente  .

Muchos novatos de mysql los  limit offset size obtendrán en lotes en forma de paginación, lo cual es bueno al principio, y lentamente, un día, la tabla de datos se vuelve extremadamente grande y puede ocurrir el  problema de paginación profunda  mencionado anteriormente.

Este escenario es la mejor solución.

Podemos ordenar todos los datos  de acuerdo con la clave principal de identificación  , luego tomarlos en lotes y usar la identificación máxima del lote actual como condición para consultar el próximo filtro.

Puedes ver el pseudocódigo

obtener datos por lotes

Para esta operación, puede usar el índice de clave principal para ubicar la identificación cada vez y luego recorrer 100 datos en el futuro, de modo que no importa cuántos miles de datos, el rendimiento de la consulta sea muy estable.

lote obtener la tabla de usuarios en lotes

Si es para mostrar la paginación a los usuarios.

Si la demanda original detrás de la paginación profunda es solo la función que el gerente de productos quiere hacer una página de visualización, como una página de visualización de productos, entonces deberíamos tener una buena batalla con el gerente de productos.

Qué tipo de cambio de página debe pasarse después de más de 100.000, lo que obviamente es una demanda irrazonable.

¿Es posible cambiar los requisitos para que se acerque más al comportamiento de uso del usuario?

Por ejemplo, la función de cambio de página que vemos cuando buscamos con Google.

En términos generales, las búsquedas de Google son básicamente dentro de las 20 páginas y, como usuario, rara vez vuelvo después de la décima página.

Referencia.

Si queremos buscar o filtrar páginas, no use mysql, use es, y también necesita controlar la cantidad de resultados que se muestran, como dentro de 10,000, para que la paginación no sea demasiado profunda.

Si por varias razones, se debe usar mysql. De la misma manera, también es necesario controlar la cantidad de resultados devueltos, como el número dentro de 1k.

De esta manera, apenas puede soportar varios cambios de página y saltos de página (como saltar repentinamente a la página 6 y luego saltar a la página 106).

Sin embargo, sería mejor si se puede hacer en forma de un producto que no admita saltos de página, como que  solo admita la página anterior o la página siguiente  .

La forma de las páginas superior e inferior.

De esta forma, podemos usar el método start_id mencionado anteriormente para obtener por lotes, y cada lote de datos comienza con start_id como posición inicial. La mayor ventaja de esta solución es que no importa cuántas páginas se pasen, la velocidad de consulta siempre es estable.

¿Suena frustrante?

¿Cómo puede ser? Envuelva esta función.

Se vuelve como Douyin, que solo se puede deslizar hacia arriba o hacia abajo, punto profesional, llamado  flujo de cascada  .

¿No será frustrante?

Resumir

  • limit offset, size Es más lento que  limit size eso, y cuanto mayor sea el valor de desplazamiento, más lenta será la velocidad de ejecución de sql.

  • Cuando el desplazamiento es demasiado grande, causará el  problema de la paginación profunda  En la actualidad, ni MySQL ni ES tienen una buena manera de resolver este problema. Solo se puede eludir limitando el número de consultas o recuperándolas en lotes.

  • Cuando se encuentre con el problema de la paginación profunda, piense más en sus requisitos originales. La mayoría de las veces, la escena de la paginación profunda no debería ocurrir y, si es necesario, afectará más al gerente de producto.

  • Si la cantidad de datos es muy pequeña, como del orden de 1k, y es poco probable que haya un gran crecimiento a largo plazo, es mejor usar  limit offset, size el plan, es bueno y se puede usar.

Referencias

"Debe conocer el principio subyacente de la cláusula de límite de MySQL" https://blog.csdn.net/qq_34115899/article/details/120727513

Al final

Con respecto a la paginación en profundidad, si tiene mejores ideas, puede decirlas en el área de comentarios.

Deja de hablar, ahoguémonos juntos en el océano del conocimiento.

Haga clic en la tarjeta de presentación a continuación para seguir la cuenta oficial: [Depuración de Xiaobai]

¿No estás satisfecho con hablar mierda en el área de mensajes? Venga a una canasta de productos secos de núcleo duro, también puede seguirme y obtener el libro de referencia de desarrollo Go.

- FINAL -

Supongo que te gusta

Origin blog.csdn.net/java_beautiful/article/details/125760762
Recomendado
Clasificación