Artículos avanzados de MySQL: que cubren índice, índice de prefijo, inserción de índice, optimización de SQL y diseño de clave principal

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

8. Priorizar los índices de cobertura

8.1 ¿Qué es un índice de cobertura?

8.1.0 Concepto 

8.0.1 En el caso de un índice de cobertura, entra en vigor el índice "no igual a"

8.0.2 En el caso de un índice de cobertura, el índice de consulta difusa izquierdo entra en vigor

8.2 Pros y contras de cubrir índices

9. Agregue un índice a una cadena.

9.1 Índice de prefijo

9.2 El índice de prefijo no puede utilizar el índice de cobertura

10. Desplazamiento del índice

10.1 Introducción

10.2 Condiciones de uso del PCI

10.3 ENCENDIDO/APAGADO DEL PIC

10.4 Casos de uso del PCI

10.5 Comparación de rendimiento entre habilitar y deshabilitar ICP

11. Índice ordinario versus índice único

11.1 Rendimiento aproximado de consultas

11.2 El rendimiento de la actualización del índice normal es mayor, cambie el búfer

11.3 Escenarios de uso del búfer de cambios

12. Optimización de SQL

12.1 Diferencia entre EXISTE y EN

12.2 Recomendar COUNT(*) o COUNT(1)

12.3 Sugerir SELECT(campo) en lugar de SELECT(*)

12.4 Efecto del LÍMITE 1 en la optimización

12.5 Utilice COMMIT con más frecuencia

13. Ideas de diseño clave primarias

13.1 Desventajas de la clave primaria de incremento automático

13.2 Intente no utilizar campos comerciales como claves principales

13.3 Diseño de clave principal del número de pedido de Taobao

13.4 Diseño de clave primaria recomendado

13.4.1 Selección de estrategia de clave principal de negocio central y no central

13.4.2 Características del UUID

13.4.3 Esquema de clave primaria de MySQL 8.0: UUID ordenados

13.4.4 Esquema de clave primaria antes de MySQL8.0: asignación manual

13.3.5 Algoritmo de copo de nieve


8. Priorizar los índices de cobertura

8.1 ¿Qué es un índice de cobertura?

8.1.0 Concepto 

Índice de cobertura: un índice que contiene datos que satisfacen los resultados de la consulta se denomina índice de cobertura y no requiere operaciones como regresar a la tabla.

Los índices son una forma de buscar filas de manera eficiente, pero en general las bases de datos también pueden usar índices para buscar datos para una columna, por lo que no es necesario leer la fila completa. Después de todo, los nodos hoja del índice almacenan los datos que indexan; cuando los datos deseados se pueden obtener leyendo el índice, no es necesario leer la fila.

El índice de cobertura es una forma de índice no agrupado, que incluye todas las columnas utilizadas en las cláusulas SELECT, JOIN y WHERE de la consulta (es decir, los campos indexados son exactamente los campos involucrados en las condiciones de la consulta cubierta). En pocas palabras, la columna de índice + clave principal contiene las columnas consultadas entre SELECT y FROM .

8.0.1 En el caso de un índice de cobertura, entra en vigor el índice "no igual a"

En el caso de que no haya un índice de cobertura, el índice "no igual a" no es válido:

En ausencia de un índice de cobertura, el uso de "no igual a" hace que el índice falle. Porque si usa un índice, debe atravesar todos los nodos de hoja en el árbol de índice B+ no agrupado por turno, la complejidad del tiempo es O (n) y debe volver a la tabla después de encontrar el registro. no es tan bueno como el escaneo completo de la tabla, por lo que el optimizador de consultas elige un escaneo completo de la tabla.

CREATE INDEX idx_age_name ON student(age, NAME);
#查所有字段,并且使用“不等于”,索引失效
EXPLAIN SELECT * FROM student WHERE age <> 20;

En el caso de un índice de cobertura, entra en vigor el índice "no igual a":

Índice de cobertura, los dos campos a verificar están cubiertos por el índice conjunto y el rendimiento es mayor. Aunque todavía es necesario recorrer todos los nodos hoja en el árbol de índice B+ no agrupado por turno, la complejidad del tiempo es O (n), pero no es necesario devolver la tabla, la eficiencia general es mayor que sin el índice. y el optimizador de consultas vuelve a utilizar el índice.

CREATE INDEX idx_age_name ON student(age, NAME);
#查的两个字段正好被联合索引“idx_age_name ”覆盖了,索引成功
EXPLAIN SELECT age,name FROM student WHERE age <> 20;

8.0.2 En el caso de un índice de cobertura, el índice de consulta difusa izquierdo entra en vigor

En el caso de que no haya un índice de cobertura, la consulta difusa de la izquierda hace que el índice falle.

#没覆盖索引的情况下,左模糊查询导致索引失效
CREATE INDEX idx_age_name ON student(age, NAME);
EXPLAIN SELECT * FROM student WHERE NAME LIKE '%abc';

En el caso de un índice de cobertura, el índice de consulta difusa izquierdo entra en vigor.

La razón principal también es que el árbol de índice B + no agrupado atraviesa los nodos hoja sin regresar a la tabla, la eficiencia será mayor que la del escaneo completo de la tabla y el optimizador de consultas elige una solución de alta eficiencia.

#有覆盖索引的情况下,左模糊查询索引生效
CREATE INDEX idx_age_name ON student(age, NAME);
EXPLAIN SELECT id,age,NAME FROM student WHERE NAME LIKE '%abc';

Todo lo anterior usa el índice declarado, pero la siguiente situación no es el caso: la columna de consulta todavía tiene más classIds y el resultado es que el índice no se usa:

CREATE INDEX idx_age_name ON student(age, NAME);
EXPLAIN SELECT id,age,NAME,classId FROM student WHERE NAME LIKE '%abc';

8.2 Pros y contras de cubrir índices

beneficio:

1. Evite volver a la tabla (consulta secundaria para indexar la tabla Innodb)

Innodb se almacena en el orden del índice agrupado. Para lnnodb, el índice secundario almacena la información de la clave principal de la fila en el nodo hoja. Si usa el índice secundario para consultar datos, después de encontrar el valor de clave correspondiente, también es Es necesario realizar una consulta secundaria a través de la clave primaria para obtener los datos que realmente necesitamos.

En el índice de cobertura, los datos requeridos se pueden obtener en el valor clave del índice secundario, evitando la consulta secundaria de la clave primaria, reduciendo las operaciones de IO y mejorando la eficiencia de la consulta.

2. La IO aleatoria se puede cambiar a IO secuencial para acelerar la eficiencia de las consultas

Dado que el índice de cobertura se almacena en el orden del valor clave, para la búsqueda de rango con uso intensivo de IO, los datos I0 de cada fila se leen del disco de forma aleatoria. Los IO leídos se convierten en IO secuenciales para las búsquedas de índice.

Dado que un índice de cobertura puede reducir la cantidad de búsquedas en árboles y mejorar significativamente el rendimiento de las consultas, el uso de un índice de cobertura es un método común de optimización del rendimiento.

Desventajas:

Deben analizarse en detalle cuestiones específicas:

Siempre hay un costo por mantener campos indexados. Por lo tanto, hay que considerar algunas compensaciones al establecer cuántos índices respaldar los índices de cobertura. Este es el trabajo del DBA empresarial o arquitecto de datos empresariales.

9. Agregue un índice a una cadena.

9.1 Índice de prefijo

Hay una mesa de profesores, la definición de la tabla es la siguiente:

create table teacher(
ID bigint unsigned primary key,
email varchar(64),
...
)engine=innodb;

El profesor debe iniciar sesión con una dirección de correo electrónico, por lo que debe aparecer una declaración similar a esta en el código comercial:

mysql> select col1, col2 from teacher where email='xxx';

Si no hay ningún índice en el campo de correo electrónico, entonces esta declaración solo puede realizar un escaneo completo de la tabla .

MySQL admite índices de prefijos. De forma predeterminada, si crea un índice sin especificar una longitud de prefijo, el índice contendrá la cadena completa.

mysql> alter table teacher add index index1(email);
#或
mysql> alter table teacher add index index2(email(6));

¿Cuál es la diferencia entre estas dos definiciones diferentes en términos de estructura y almacenamiento de datos? La siguiente figura es un diagrama esquemático de estos dos índices.

así como

Si se utiliza index1 (el índice contiene la cadena completa), el orden de ejecución es el siguiente:

  1. Encuentre el registro que satisfaga el valor de índice de '[email protected]' del árbol de índice de index1 y obtenga el valor de ID2;
  2. Regrese a la tabla para encontrar la fila cuyo valor de clave principal es ID2 en la clave principal, juzgue que el valor del correo electrónico es correcto y agregue este registro de fila al conjunto de resultados;
  3. Tome el siguiente registro en la posición que acaba de encontrar en el árbol de índice de index1 y descubra que la condición de correo electrónico = '[email protected]' ya no se cumple y el ciclo finaliza.

En este proceso, sólo es necesario recuperar datos una vez del índice de clave primaria, por lo que el sistema considera que solo se ha escaneado una fila.

Si se utiliza index2 (el índice contiene el prefijo de cadena email(6)), la secuencia de ejecución es la siguiente:

  1. Encuentre el registro que satisfaga el valor de índice de 'zhangs' en el árbol de índice index2, y el primero encontrado es ID1;
  2. Regrese a la tabla y busque la fila cuyo valor de clave principal es ID1 en la clave principal, determine que el valor del correo electrónico no es '[email protected]' y descarte el registro en esta fila;
  3. Tome el siguiente registro en la ubicación que acaba de encontrar en el índice 2 y descubra que todavía es 'zhangs', extraiga el ID2 y luego regrese a la tabla para buscar la fila completa en el índice de ID y luego determine que el valor es correcto. esta vez, y agregue esta fila al conjunto de resultados;
  4. Repita el paso anterior hasta que el valor obtenido en index2 no sea 'zhangs' , el ciclo finaliza.

Es decir, utilizar el índice de prefijo y definir la longitud puede ahorrar espacio sin agregar demasiados costos de consulta adicionales. El grado de discriminación ya se ha mencionado antes, y cuanto mayor sea el grado de discriminación, mejor . Porque cuanto mayor sea el grado de discriminación, menos valores clave duplicados.

9.2 El índice de prefijo no puede utilizar el índice de cobertura

Debido a que los datos encontrados por el árbol de índice no agrupado son el prefijo y la identificación, el prefijo no son datos completos y deben devolverse al árbol de índice agrupado.

Por lo tanto, el uso de un índice de prefijo no necesita optimizar el rendimiento de la consulta del índice de cobertura, que también es un factor que debe tenerse en cuenta al elegir si se utiliza un índice de prefijo.

10. Desplazamiento del índice

10.1 Introducción

Index Condition Pushdown (ICP, Index Condition Pushdown) es una nueva característica en MySQL 5.6. Es una forma optimizada de utilizar índices para filtrar datos en la capa del motor de almacenamiento.

  • Si no hay ICP : cuando un campo del índice conjunto es una consulta difusa (difusa no izquierda), después de juzgar el campo, los siguientes campos no se pueden usar para el juicio de condición directa y el juicio debe realizarse después de regresar a la mesa.
  • Después de habilitar ICP : cuando un campo en el índice conjunto es una consulta difusa (no se deja difusa), después de juzgar el campo, los siguientes campos se pueden juzgar directamente. Después de filtrar el juicio, regrese a la tabla para verificar el condiciones de los campos no incluidos en el juez de índice conjunto. El principal punto de optimización es filtrar antes de regresar a la tabla para reducir la cantidad de veces que se regresa a la tabla. Aplicación principal: la consulta difusa (difusa no izquierda) hace que los campos detrás del campo en el índice estén desordenados y deben juzgarse regresando a la tabla. Sin embargo, si se usa el índice pushdown, no es necesario regresar a la tabla y el juicio se realiza directamente en el árbol de índice conjunto.

Si no hay ICP , el motor de almacenamiento atravesará el índice para ubicar las filas en la tabla base y las devolverá al servidor MySQL, y el servidor MySQL evaluará si las condiciones detrás de WHERE están reservadas.
Después de habilitar ICP , si parte de la condición WHERE se puede filtrar usando solo las columnas del índice, el servidor MySQL colocará esta parte de la condición WHERE en el filtro del motor de almacenamiento. Luego, el motor de almacenamiento filtra los datos utilizando las entradas del índice y lee las filas de la tabla solo si se cumple esta condición.

Beneficios: ICP puede reducir la cantidad de veces que el motor de almacenamiento debe acceder a la tabla base y la cantidad de veces que el servidor MySQL debe acceder al motor de almacenamiento. Sin embargo, el efecto de aceleración de ICP depende de la proporción de datos filtrados por ICP en el motor de almacenamiento. 

Ejemplo:

Índices conjuntos que no admiten la inserción de índices: por ejemplo, índice (nombre, edad), nombre de consulta como 'z%' y edad=? , la consulta difusa hace que la edad esté desordenada. Al consultar el árbol de índice conjunto, solo se busca el nombre, y las siguientes edades no pueden juzgarse directamente por la condición, y la edad debe juzgarse después de regresar a la tabla.

Y el índice conjunto que admite la inserción de índice: por ejemplo, índice (nombre, edad), nombre de consulta como 'z%' y edad y dirección, no solo verifique el nombre al consultar el árbol de índice conjunto, sino que también juzgue la edad posterior. filtrar y devolver la dirección de juicio de la tabla.

CREATE INDEX idx_name_age ON student(name,age);
#索引失败;非覆盖索引时,左模糊导致索引失效
EXPLAIN SELECT * FROM student WHERE name like '%bc%' AND age=30;
#索引成功;MySQL5.6引入索引下推,where后面的name和age都在联合索引里,可以又过滤又索引,不用回表,索引生效
EXPLAIN SELECT * FROM student WHERE `name` like 'bc%' AND age=30;
#索引成功;name走索引,age用到索引下推过滤,classid不在联合索引里,需要回表。
EXPLAIN SELECT * FROM student WHERE `name` like 'bc%' AND age=30 AND classid=2;

Beneficios:  en algunos escenarios, ICP puede reducir en gran medida la cantidad de resultados de tablas y mejorar el rendimiento. ICP puede reducir la cantidad de veces que el motor de almacenamiento debe acceder a la tabla base y la cantidad de veces que el servidor MySQL debe acceder al motor de almacenamiento. Sin embargo, el efecto de aceleración de ICP depende de la proporción de datos filtrados por ICP en el motor de almacenamiento .

10.2 Condiciones de uso del PCI

  • El tipo de acceso a la tabla es rango, ref, eq_ref o ref_or_null.
  • Motor de almacenamiento: ICP se puede utilizar para motores de almacenamiento InnDB y MyISAM.
  • Se requieren índices secundarios: para las tablas InnoDB, ICP solo se usa para índices secundarios. El objetivo de ICP es reducir la cantidad de lecturas de filas completas, reduciendo así las operaciones de E/S.
  • No debe ser un índice de cobertura: cuando SQL utiliza un índice de cobertura, no se admite el método de optimización ICP. Porque usar ICP en este caso no reducirá la E/S.
  • Las condiciones para subconsultas correlacionadas no pueden utilizar ICP
  • Debe ser la versión 5.6 y superior: MySQL versión 5.6 se introduce y habilita de forma predeterminada, y las versiones anteriores no admiten la inserción de índice.
  • El campo donde debe estar en la columna de índice: ICP no puede filtrar todas las condiciones donde. Si el campo de la condición donde no está en la columna de índice, aún es necesario leer los registros de la tabla completa en el servidor para donde filtrar.

10.3 ENCENDIDO/APAGADO DEL PIC

  • La inserción de condiciones de índice está habilitada de forma predeterminada. Se puede controlar configurando la variable del sistema optimizador_switch : index_condition_pushdown
# 打开索引下推
SET optimizer_switch = 'index_condition_pushdown=on';

# 关闭索引下推
SET optimizer_switch = 'index_condition_pushdown=off';
  • Cuando la condición de índice se presiona hacia abajo, el contenido de la columna Extra en el resultado de la declaración EXPLAIN se muestra como Usando condición de índice .

10.4 Casos de uso del PCI

  • Índice de clave primaria (diagrama simplificado)

Índice secundario zip_last_first (aquí se omiten el diagrama simplificado, las páginas de datos y otra información)

10.5 Comparación de rendimiento entre habilitar y deshabilitar ICP

11. Índice ordinario versus índice único

Desde el punto de vista del rendimiento, ¿elige un índice único o un índice normal? ¿Cuál es la base de la elección?

Supongamos que tenemos una tabla cuya clave principal es ID. Hay un campo k en la tabla y hay un índice en k, suponiendo que los valores en el campo k no se repiten.

La declaración de creación de tabla para esta tabla es:

mysql> create table test(
id int primary key,
k int not null,
name varchar(16),
index (k)
)engine=InnoDB;

Los valores (ID,k) de R1 ~ R5 en la tabla son (100,1), (200,2), (300,3), (500,5) y (600,6) respectivamente.

11.1 Rendimiento aproximado de consultas

Supongamos que la declaración para ejecutar la consulta es seleccionar ID de la prueba donde k = 5.

  • Para un índice normal, después de encontrar el primer registro (5500) que satisface la condición, es necesario buscar el siguiente registro hasta encontrar el primer registro que no cumple la condición k=5.
  • Para un índice único, dado que el índice define la unicidad, después de encontrar el primer registro que cumpla la condición, la búsqueda se detendrá.

Entonces, ¿cuál es la brecha de rendimiento provocada por esta diferencia? La respuesta es muy poco .

11.2 El rendimiento de la actualización del índice normal es mayor, cambie el búfer

Escribir caché (cambiar búfer):

Cuando es necesario actualizar una página de datos, si la página de datos está en la memoria, se actualizará directamente, y si la página de datos no está en la memoria, InooDB almacenará en caché estas operaciones de actualización en el búfer de cambios sin afectar la coherencia de los datos que No es necesario leer esta página de datos desde el disco. Cuando la próxima consulta necesite acceder a esta página de datos, lea la página de datos en la memoria y luego ejecute las operaciones relacionadas con esta página en el búfer de cambios. De esta forma se puede garantizar la exactitud de la lógica de los datos.

fusión: el proceso de aplicar la operación en el búfer de cambios a la página de datos original para obtener el último resultado se llama fusión. Además de acceder a esta página de datos se activará la fusión, el sistema tiene un hilo en segundo plano que se fusionará periódicamente. La operación de combinación también se realiza durante el cierre normal de la base de datos.

Si la operación de actualización se puede registrar primero en el búfer de cambios para reducir las lecturas del disco , la velocidad de ejecución de la declaración mejorará significativamente. Además, leer datos en la memoria requiere un grupo de búfer, por lo que este método también puede evitar la ocupación de memoria y mejorar su utilización.

La actualización del índice único no puede usar el búfer de cambios ; de hecho, solo se pueden usar índices ordinarios.

Has una distincion:

  • Leer datos utilizando el grupo de búfer del grupo de búfer ;
  • El registro de rehacer tiene un búfer de registro de rehacer , que consiste en escribir los datos actualizados en el grupo de búfer en el búfer de registro de rehacer. Cuando se confirma la transacción, el búfer de registro de rehacer se vacía en el archivo de registro de rehacer o en el caché de página de acuerdo con el vaciado. estrategia.

11.3 Escenarios de uso del búfer de cambios

  • ¿Cómo elegir un índice ordinario y un índice único? De hecho, no hay diferencia en las capacidades de consulta entre estos dos tipos de índices . La consideración principal es el impacto en el rendimiento de las actualizaciones . Por lo tanto, se recomienda que intente elegir un índice común .

  • En el uso real, se encontrará que el uso combinado de índices ordinarios y búfer de cambios es muy obvio para actualizar y optimizar tablas con grandes cantidades de datos .

  • No es adecuado para situaciones de búfer de cambios: si todas las actualizaciones van seguidas inmediatamente de consultas a este registro, entonces debe desactivar el búfer de cambios. En otros casos, el búfer de cambios puede mejorar el rendimiento de la actualización.

  • Cuando se confirma la transacción, la operación de cambio del búfer también se registrará en el registro de rehacer , por lo que cuando se recupere el fallo, también se podrá recuperar el búfer de cambio.

  • Dado que el índice único no utiliza el mecanismo de optimización del búfer de cambios, si el negocio es aceptable, se recomienda dar prioridad a los índices no únicos desde una perspectiva de rendimiento. Pero si "el negocio puede no estar garantizado", ¿cómo afrontarlo?

    • En primer lugar, la corrección empresarial tiene prioridad. Nuestra premisa es que "se garantiza que el código comercial no escribirá datos duplicados" para discutir problemas de rendimiento. Si el negocio no se puede garantizar, o el negocio requiere que la base de datos sea una restricción, entonces no queda más remedio que crear un índice único. En este caso, la importancia de esta sección es brindarle una idea adicional para la solución de problemas si se inserta lentamente una gran cantidad de datos y la tasa de aciertos de la memoria es baja.
    • Luego, en algunos escenarios de "biblioteca de archivos", puede considerar el uso de índices únicos. Por ejemplo, los datos en línea solo deben conservarse durante medio año y luego los datos históricos se almacenan en la biblioteca de archivos. En este punto, archivar datos ya garantiza que no haya conflictos de claves únicas. Para mejorar la eficiencia del archivado, puede considerar cambiar el índice único de la tabla por un índice común.

12. Optimización de SQL

12.1 Diferencia entre EXISTE y EN

pregunta:

No entiendo muy bien qué situación debería usar EXISTS y qué situación debería usar IN. ¿El criterio de selección es ver si se puede utilizar el índice de la tabla?

respuesta:

12.2 Recomendar COUNT(*) o COUNT(1)

Utilice COUNT(1), COUNT(*) tanto como sea posible para contar el número de filas: cuando COUNT(1), COUNT(*), el optimizador de consultas dará prioridad a seleccionar el árbol de índice secundario con índices y ocupando el más pequeño espacio para estadísticas Las estadísticas del árbol de índice agrupado se utilizan al acceder a árboles de índice no agrupados, lo que ocupa mucho espacio. Por supuesto, también se puede utilizar COUNT (campo de índice secundario de espacio mínimo), pero el problema no es tan bueno como la selección automática por parte del optimizador.

SELECT COUNT(*) FROM student;
SELECT COUNT(1) FROM student;

 Pregunta: Hay tres formas de contar el número de filas en una tabla de datos en MySQL: SELECT COUNT(*), SELECT COUNT(1) y SELECT COUNT (campos específicos) ¿Cuál es la eficiencia de consulta entre estos tres métodos?

Respuesta: Si desea contar el número de filas de datos no nulos en un determinado campo, es otra cuestión, después de todo, la premisa de comparar la eficiencia de la ejecución es que los resultados son los mismos.

COUNT(*) y COUNT(1): COUNT(*) y COUNT(1) realizan COUNT(*) en todos los resultados , y esencialmente no hay diferencia entre COUNT(*) y COUNT(1) (el tiempo de ejecución de los dos pueden tener una ligera diferencia, pero aún se puede considerar la eficiencia de ejecución de los dos como igual). Si hay una cláusula WHERE, contará todas las filas de datos que cumplan las condiciones de filtrado. Si no hay una cláusula WHERE, contará el número de filas de datos en la tabla de datos.

Las estadísticas de MylSAM solo necesitan O(1): si es el motor de almacenamiento MylSAM, el número de filas en la tabla de datos estadísticos solo necesita la complejidad de O(1) , porque cada tabla de datos MyISAM tiene una metainformación para almacenar el valor de recuento de filas . La coherencia está garantizada mediante bloqueos a nivel de tabla. Si es un motor de almacenamiento InnoDB, debido a que innoDB admite transacciones y usa bloqueos de nivel de fila y mecanismo MVCC, no puede mantener una variable de recuento de filas como MyISAM, por lo que necesita escanear toda la tabla, que es de complejidad O (n) y bucle + El conteo se realiza a modo de conteo.

Sugerencia de selección: en ImnoDB, si usa COUNT (campo específico) para contar el número de filas de datos, intente usar índices secundarios . Debido a que la clave principal es un índice agrupado y los nodos hoja del índice agrupado contienen el registro completo, la cantidad de datos que se cargarán en la memoria durante las estadísticas es mayor y el rendimiento es peor. Para COUNT(*) y COUNT(1), no necesitan buscar filas específicas, solo cuentan el número de filas, y el sistema utilizará automáticamente el índice secundario que ocupa menos espacio para las estadísticas . Si hay varios índices secundarios, se utilizará el índice secundario con key_len más pequeño para escanear. Cuando no hay un índice secundario, se utilizará el índice de clave principal para las estadísticas.

12.3 Sugerir SELECT(campo) en lugar de SELECT(*)

En la consulta de tabla, se recomienda especificar los campos, no use * como lista de campos de la consulta, se recomienda usar la consulta SELECT <lista de campos>. razón:

① Durante el proceso de análisis, MySQL consultará el diccionario de datos para convertir "*" en todos los nombres de columnas en secuencia , lo que consumirá muchos recursos y tiempo.

② No se puede utilizar el índice de cobertura

12.4 Efecto del LÍMITE 1 en la optimización

Está dirigido a declaraciones SQL que escanean toda la tabla . Si puede estar seguro de que solo hay un conjunto de resultados , al agregar LÍMITE 1, el escaneo no continuará cuando se encuentre un resultado, lo que acelerará la consulta.

Si la tabla de datos ha establecido un índice único para el campo, puede consultar a través del índice. Si no escanea toda la tabla, no necesita agregar LÍMITE 1.

12.5 Utilice COMMIT con más frecuencia

Siempre que sea posible, utilice COMMIT tanto como sea posible en su programa, de modo que se mejore el rendimiento del programa y se reduzca la demanda debido a los recursos liberados por COMMIT.

Recursos publicados por COMMIT:

  • Información utilizada para restaurar datos en el segmento de reversión.
  • bloqueos adquiridos por declaraciones de programa
  • Espacio en el búfer de registro de rehacer/deshacer
  • Gestionar el gasto interno en los 3 recursos anteriores

13. Ideas de diseño clave primarias

Hablemos de una pregunta práctica: ¿Cómo se diseña la clave principal para la base de datos de Taobao?

Ciertas respuestas erróneas y escandalosas siguen circulando en Internet año tras año, e incluso se convierten en las llamadas regulaciones militares de MySQL. Entre ellos, uno de los errores más obvios tiene que ver con el diseño de la clave principal de MySQL.

La respuesta de la mayoría de las personas es muy segura: use BIGINT de 8 bytes como clave principal en lugar de INT. ¡Mal !

Esta respuesta es solo a nivel de base de datos, sin pensar en la clave principal desde una perspectiva empresarial . ¿Es la clave principal una ID de incremento automático? En la actualidad, es posible que el uso del incremento automático como clave principal ni siquiera pase el diseño arquitectónico .

13.1 Desventajas de la clave primaria de incremento automático

La ID de incremento automático se utiliza como clave principal, lo cual es fácil de entender. Casi todas las bases de datos admiten el tipo de incremento automático, pero la implementación es diferente. Además de ser simples, las ID autoincrementales tienen desventajas. En general, existen los siguientes problemas:

  • baja confiabilidad

    Hay un problema con el retroceso de ID de incremento automático, que no se solucionó hasta la última versión de MySQL 8.0.

    Problema de retroceso: por ejemplo, inserte tres filas de datos cuyas claves principales sean 1, 2 y 3 en una nueva tabla. En este momento, use SHOW CREATE TABLEel comando para verificar que el valor de la tabla AUTO_INCREMENTsea 4, lo cual no es un problema.

    Luego elimine la fila de datos con ID = 3 y AUTO_INCREMENTel valor consultado nuevamente sigue siendo 4, lo cual no es un problema.

    Pero si reinicia MySQL, este valor volverá a cambiar a 3 en lugar de 4, y se producirá un retroceso.

  • baja seguridad

    La interfaz expuesta puede ser muy fácil de adivinar la información correspondiente . Por ejemplo, una interfaz como /Usuario/1/ puede adivinar fácilmente el valor del ID de usuario y el número total de usuarios, y también puede rastrear datos fácilmente a través de la interfaz.

  • bajo rendimiento

    La ID de incremento automático tiene un rendimiento deficiente y debe generarse en el lado del servidor de la base de datos.

  • Se requieren funciones de ejecución adicionales para conocer el valor de autoincremento, lo que afecta el rendimiento.

    La empresa también necesita ejecutar una función similar a last_insert_id() para conocer el valor de autoincremento recién insertado, lo que requiere una interacción de red más. En un sistema masivamente concurrente, una declaración SQL más significa una sobrecarga de rendimiento más .

  • Lo global no es único: la competencia de bloqueos que aumenta automáticamente afecta el rendimiento durante la alta concurrencia

    El punto más importante es que el ID de incremento automático es único localmente, único solo en la instancia de base de datos actual, no globalmente único y único entre cualquier servidor. Para los sistemas distribuidos actuales, esto es simplemente una pesadilla.

  • El incremento automático ya no es aplicable cuando se migran la subbase de datos y la tabla.

13.2 Intente no utilizar campos comerciales como claves principales

Para identificar de forma única la información de un miembro, se debe establecer una clave principal para la tabla de información del miembro. Entonces, ¿cómo configurar la clave principal de esta tabla para lograr nuestro objetivo ideal? Aquí consideramos el ámbito empresarial como clave principal.

Los datos de la tabla son los siguientes:

En esta tabla, ¿qué campo es más apropiado?

  • Seleccionar número de tarjeta (cardno)

El número de tarjeta de membresía (cardno) parece más apropiado, porque el número de tarjeta de membresía no puede estar vacío y es único, lo que puede usarse para identificar un registro de membresía.

mysql> CREATE TABLE demo.membermaster
-> (
-> cardno CHAR(8) PRIMARY KEY, -- 会员卡号为主键
-> membername TEXT,
-> memberphone TEXT,
-> memberpid TEXT,
-> memberaddress TEXT,
-> sex TEXT,
-> birthday DATETIME
-> );
Query OK, 0 rows affected (0.06 sec)

Diferentes números de tarjeta de membresía corresponden a diferentes miembros, y el campo "cardno" identifica de forma única a un determinado miembro. Si este es el caso, el número de tarjeta de membresía corresponde al miembro uno por uno y el sistema puede funcionar normalmente.

Pero la situación real es que el número de tarjeta de miembro puede reutilizarse . Por ejemplo, Zhang San se mudó de su dirección original debido a un cambio de trabajo y ya no iba a la tienda del comerciante a consumir (se le devolvió la tarjeta de membresía), por lo que Zhang San ya no era miembro de la tienda del comerciante. Sin embargo, el comerciante no quería que la tarjeta de membresía estuviera vacía, por lo que envió la tarjeta de membresía con el número de tarjeta "10000001" a Wang Wu.

Desde el punto de vista del diseño del sistema, este cambio solo modifica la información del miembro cuyo número de tarjeta es "10000001" en la tabla de información del miembro y no afectará la coherencia de los datos. Es decir, si modifica la información del miembro cuyo número de tarjeta de membresía es "10000001", cada módulo del sistema obtendrá la información del miembro modificada y no habrá "algunos módulos obtienen la información del miembro antes de la modificación, y algunos módulos obtener la información modificada del miembro posterior, lo que resulta en inconsistencia de datos dentro del sistema ". Por tanto, desde el nivel del sistema de información, no hay ningún problema.
Pero desde el nivel empresarial del uso del sistema, existen grandes problemas que afectarán a los comerciantes.

Por ejemplo, tenemos una tabla de flujo de ventas (trans), que registra todos los detalles del flujo de ventas. El 1 de diciembre de 2020, Zhang San compró un libro en la tienda y gastó 89 yuanes. Luego, hay un registro de la compra de libros de Zhang San en el sistema, como se muestra a continuación:

A continuación, revisemos los registros de ventas de membresías el 1 de diciembre de 2020:

mysql> SELECT b.membername,c.goodsname,a.quantity,a.salesvalue,a.transdate
-> FROM demo.trans AS a
-> JOIN demo.membermaster AS b
-> JOIN demo.goodsmaster AS c
-> ON (a.cardno = b.cardno AND a.itemnumber=c.itemnumber);
+------------+-----------+----------+------------+---------------------+
| membername | goodsname | quantity | salesvalue | transdate |
+------------+-----------+----------+------------+---------------------+
|     张三   | 书         | 1.000    | 89.00      | 2020-12-01 00:00:00 |
+------------+-----------+----------+------------+---------------------+
1 row in set (0.00 sec)

Si se vuelve a emitir la tarjeta de membresía "10000001" a Wang Wu, cambiaremos el formulario de información de membresía. Al generar una consulta:

mysql> SELECT b.membername,c.goodsname,a.quantity,a.salesvalue,a.transdate
-> FROM demo.trans AS a
-> JOIN demo.membermaster AS b
-> JOIN demo.goodsmaster AS c
-> ON (a.cardno = b.cardno AND a.itemnumber=c.itemnumber);
+------------+-----------+----------+------------+---------------------+
| membername | goodsname | quantity | salesvalue | transdate |
+------------+-----------+----------+------------+---------------------+
| 王五        | 书        | 1.000    | 89.00      | 2020-12-01 00:00:00 |
+------------+-----------+----------+------------+---------------------+
1 row in set (0.01 sec)

El resultado obtenido esta vez es: Wang Wu compró un libro el 1 de diciembre de 2020 y gastó 89 yuanes. ¡Obviamente mal! Conclusión: no utilice el número de tarjeta de membresía como clave principal.

  • Seleccione el número de teléfono o número de identificación del miembro

¿Se puede utilizar un número de teléfono de miembro como clave principal? De ninguna manera. En la operación real, el operador también recupera el número de teléfono móvil y lo vuelve a emitir a otros.

¿Qué pasa con el número de identificación? Parece posible. Debido a que la tarjeta de identificación nunca se repetirá, existe una correspondencia uno a uno entre el número de identificación y una persona. Pero el problema es que el número de identificación pertenece a la privacidad personal y es posible que los clientes no estén dispuestos a dártelo. Si es obligatorio que los miembros registren sus números de identificación, muchos clientes se alejarán. De hecho, el teléfono del cliente también tiene este problema, por lo que permitimos que el número de identificación y el número de teléfono estén vacíos al diseñar el formulario de información del miembro.

Por lo tanto, se recomienda no utilizar campos relacionados con el negocio como claves principales . Después de todo, como técnicos de diseño de proyectos, ninguno de nosotros puede predecir qué campo comercial se repetirá o reutilizará debido a los requisitos comerciales del proyecto durante todo el ciclo de vida del proyecto.

Experiencia: cuando comienza a usar MySQL por primera vez, muchas personas son propensas a cometer el error de usar campos comerciales como claves principales. Dan por sentado que comprenden las necesidades comerciales, pero la situación real a menudo es inesperada y el costo del cambio. la configuración de la clave principal es muy alta .

13.3 Diseño de clave principal del número de pedido de Taobao

En el negocio de comercio electrónico de Taobao, el servicio de pedidos es un negocio principal. Disculpe, ¿cómo se diseña la clave principal de Taobao en la tabla de pedidos? ¿Es una identificación de incremento automático?

Abra Taobao y mire la información del pedido:

Como puede verse en la figura anterior, el número de pedido no es una ID de incremento automático . Veamos en detalle los 4 números de pedido anteriores:

1550672064762308113
1481195847180308113
1431156171142308113
1431146631521308113

El número de pedido tiene 19 dígitos y los últimos 5 dígitos del pedido son todos iguales, 08113. Y los primeros 14 dígitos del número de pedido aumentan monótonamente.

Adivine audazmente, el diseño de identificación del pedido de Taobao debería ser:

订单ID = 时间 + 去重字段 + 用户ID后6位尾号

Un diseño de este tipo puede ser globalmente único y extremadamente amigable para las consultas de sistemas distribuidos.

13.4 Diseño de clave primaria recomendado

13.4.1 Selección de estrategia de clave principal de negocio central y no central

Negocio no principal : el ID de incremento automático de la clave principal de la tabla correspondiente, como alarmas, registros, monitoreo y otra información.

Negocio principal  : el diseño de la clave principal debe ser al menos globalmente único y creciente de manera monótona. Se garantiza que la unicidad global será única entre cada sistema, y ​​el aumento monótono es esperar que la inserción no afecte el rendimiento de la base de datos. Se recomienda utilizar MySQL8.0 para transformarlo en un UUID ordenado. Específicamente, utilice la función uuid_to_bin(@uuid,true) para convertir un UUID en un UUID ordenado.

13.4.2  Características del UUID

Aquí se recomienda el diseño de clave primaria más simple: UUID.

Único globalmente , ocupa 36 bytes, los datos están desordenados y el rendimiento de inserción es deficiente.

Reconocer UUID:

  • ¿Por qué los UUID son únicos a nivel mundial?
  • ¿Por qué UUID ocupa 36 bytes?
  • ¿Por qué los UUID están desordenados?

La composición UUID de la base de datos MySQL es la siguiente:

UUID = 时间+UUID版本(16字节)- 时钟序列(4字节) - MAC地址(12字节)

Tomemos el valor UUID e0ea12d4-6473-11eb-943c-00155dbaa39d como ejemplo:

¿Por qué los UUID son únicos a nivel mundial? 

La parte de tiempo en UUID ocupa 60 bits y la marca de tiempo almacenada es similar a TIMESTAMP, pero representa el recuento de 100 ns desde 1582-10-15 00:00:00.00 hasta el presente. Se puede ver que la precisión temporal del almacenamiento UUID es mayor que la de TIMESTAMPE y la probabilidad de duplicación en la dimensión temporal se reduce a 1/100 ns .

La secuencia del reloj es para evitar la posibilidad de que el reloj se retrase y cause duplicación de tiempo . La dirección MAC se utiliza para lograr unicidad global .

¿Por qué UUID ocupa 36 bytes?

Los UUID se almacenan en términos de cadenas y están diseñados con cadenas "-" inútiles, por lo que se requiere un total de 36 bytes.

¿Por qué los UUID son aleatorios y desordenados?

Porque en el diseño de UUID, el bit de tiempo bajo se coloca al frente , y los datos en esta parte siempre cambian y están desordenados.

13.4.3 Esquema de clave primaria de MySQL 8.0: UUID ordenados

Transformación en orden: si se intercambian los bits altos y bajos del tiempo, el tiempo aumenta monótonamente y aumenta monótonamente. MySQL 8.0 puede reemplazar el método de almacenamiento de tiempo bajo y tiempo alto, de modo que el UUID sea un UUID ordenado.

Optimice la ocupación del espacio: MySQL 8.0 también resuelve el problema de ocupación del espacio del UUID, elimina la cadena "-" sin sentido en la cadena UUID y guarda la cadena en tipo binario, reduciendo así el espacio de almacenamiento a 16 bytes.

Las funciones anteriores se pueden implementar a través de la función uuid_to_bin proporcionada por MySQL8.0 . De manera similar, MySQL también proporciona la función bin_to_uuid para la conversión:

SET @uuid = UUID();
SELECT @uuid,uuid_to_bin(@uuid),uuid_to_bin(@uuid,TRUE);

El UUID se convierte en un UUID ordenado mediante la función uuid_to_bin(@uuid,true) . Globalmente único + monótonamente creciente , ¿no es esta la clave principal que queremos?

Prueba de rendimiento de UUID solicitada:

¿Cómo se compara el UUID ordenado de 16 bytes con el ID autoincremental anterior de 8 bytes en términos de rendimiento y espacio de almacenamiento?

Hagamos una prueba, insertemos 100 millones de datos, cada dato ocupa 500 bytes y contiene 3 índices secundarios, el resultado final es el siguiente:

En la figura anterior, podemos ver que es más rápido insertar 100 millones de UUID ordenados de datos y, en el uso comercial real, se pueden generar UUID ordenados en el lado comercial . También es posible reducir aún más el número de interacciones SQL.

Además, aunque el UUID ordenado tiene 8 bytes más que el ID autoincremental, solo aumenta el espacio de almacenamiento de 3G, lo cual es aceptable.

En el entorno actual de Internet, no se recomienda el diseño de bases de datos con ID autoincremental como clave principal. Se recomienda más una implementación globalmente única como UUID ordenado.

Además, en un sistema empresarial real, la clave principal también se puede agregar a los atributos del sistema y del negocio, como el número de cola del usuario, la información de la sala de computadoras, etc. Un diseño clave tan primario pondrá a prueba aún más el nivel del arquitecto.

13.4.4 Esquema de clave primaria antes de MySQL8.0: asignación manual

¡Asigne manualmente el campo como clave principal!

Por ejemplo, diseñe la clave principal de la tabla de membresía de cada sucursal, porque si es necesario fusionar los datos generados por cada máquina, puede ocurrir el problema de la duplicación de la clave principal.

Puede tener una tabla de información de administración en la base de datos MySQL de la sede y agregar un campo a esta tabla para registrar el valor máximo del número de membresía actual.

Al agregar un miembro, la tienda primero obtiene el valor máximo de la base de datos MySQL de la sede, agrega 1 a esta base y luego usa este valor como el "id" del nuevo miembro y, al mismo tiempo, actualiza el actual. miembro en la tabla de información de gestión de la base de datos MySQL de la sede El valor máximo del número.

De esta manera, cuando cada tienda agrega miembros, opera en los campos de la tabla de datos en la misma base de datos MySQL de la sede, lo que resuelve el problema de los conflictos de números de miembros cuando cada tienda agrega miembros.

13.3.5 Algoritmo de copo de nieve

Identificaciones ordenadas.

Un entero de 64 bits del tipo de datos Long: compuesto por un bit de signo de 1 bit, una marca de tiempo de 41 bits, una identificación de máquina en funcionamiento de 10 bits y un número de serie de 12 bits.

ventaja:

  • Ordenado: todos los identificadores generados se incrementan según la tendencia temporal
  • Distribuido y no repetitivo: no se generarán identificadores duplicados en todo el sistema distribuido.

defecto:

  • Confiar en el reloj de la máquina: confiar en el reloj de la máquina, si el reloj de la máquina se retrasa, se generarán identificaciones duplicadas.
  • Los relojes distribuidos no sincronizados provocan una falla en el incremento: incremento en una sola máquina, pero si en un entorno distribuido, es posible que los relojes de cada máquina no estén sincronizados, es posible que no sea un incremento global.
  • Pérdida de precisión: los números binarios de 64 bits se convierten a 19 dígitos en decimal, pero el front-end js solo puede garantizar la precisión de los primeros 16 dígitos. Cuando el front-end obtenga estos datos, redondeará los últimos tres dígitos. Se pierde precisión.

Supongo que te gusta

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