Articles avancés MySQL - couvrant l'index, l'index de préfixe, le refoulement d'index, l'optimisation SQL, la conception de clé primaire

la navigation:   

[Notes Java + Résumé de Stepping on the Pit] Bases Java + Avancé + JavaWeb + SSM + SpringBoot + St. Regis Takeaway + SpringCloud + Dark Horse Tourism + Guli Mall + Xuecheng Online + Chapitre avancé MySQL + Mode conception + Questions d'entretien Nioke

Table des matières

8. Prioriser les index de couverture

8.1 Qu'est-ce qu'un indice de couverture ?

8.1.0 Concept 

8.0.1 Dans le cas d'un indice couvrant, l'indice « différent de » prend effet

8.0.2 Dans le cas d'un index couvrant, l'index de requête floue gauche prend effet

8.2 Avantages et inconvénients de la couverture des index

9. Ajouter un index à une chaîne

9.1 Index des préfixes

9.2 L'index de préfixe ne peut pas utiliser l'index de couverture

10. Déroulage de l'index

10.1 Présentation

10.2 Conditions d'utilisation de l'ICP

10.3 Activation/Désactivation du PCI

10.4 Cas d'utilisation du PCI

10.5 Comparaison des performances entre l'activation et la désactivation d'ICP

11. Indice ordinaire vs indice unique

11.1 Performances approximatives des requêtes

11.2 Les performances de mise à jour de l'index ordinaire sont plus élevées, changez le tampon

11.3 Utiliser des scénarios de tampon de changement

12. Optimisation SQL

12.1 Différence entre EXISTE et IN

12.2 Recommander COUNT(*) ou COUNT(1)

12.3 Suggérer SELECT(field) au lieu de SELECT(*)

12.4 Effet de LIMIT 1 sur l'optimisation

12.5 Utiliser COMMIT plus souvent

13. Idées de conception clés primaires

13.1 Inconvénients de la clé primaire à incrémentation automatique

13.2 Essayez de ne pas utiliser les champs métier comme clés primaires

13.3 Conception de la clé primaire du numéro de commande Taobao

13.4 Conception de clé primaire recommandée

13.4.1 Sélection de la stratégie clé principale des activités principales et non essentielles

13.4.2 Caractéristiques de l'UUID

13.4.3 Schéma de clé primaire MySQL 8.0 : UUID ordonnés

13.4.4 Schéma de clé primaire avant MySQL8.0 : affectation manuelle

13.3.5 Algorithme de flocon de neige


8. Prioriser les index de couverture

8.1 Qu'est-ce qu'un indice de couverture ?

8.1.0 Concept 

Index de couverture : un index qui contient des données qui satisfont aux résultats de la requête est appelé index de couverture et ne nécessite pas d'opérations telles que le retour à la table.

Les index sont un moyen de rechercher efficacement des lignes, mais en général, les bases de données peuvent également utiliser des index pour rechercher les données d'une colonne, de sorte qu'il n'est pas nécessaire de lire la ligne entière. Après tout, les nœuds feuilles d'index stockent les données qu'ils indexent ; lorsque les données souhaitées peuvent être obtenues en lisant l'index, il n'est pas nécessaire de lire la ligne.

L'index de couverture est une forme d'index non clusterisé, qui inclut toutes les colonnes utilisées dans les clauses SELECT, JOIN et WHERE de la requête (c'est-à-dire que les champs indexés sont exactement les champs impliqués dans les conditions de requête couvertes). En termes simples, la colonne d'index + la clé primaire contient les colonnes interrogées entre SELECT et FROM .

8.0.1 Dans le cas d'un indice couvrant, l'indice « différent de » prend effet

En cas d'absence d'index couvrant, l'index "différent de" n'est pas valide :

En l'absence d'index de couverture, l'utilisation de « différent de » entraîne l'échec de l'index. Parce que si vous utilisez un index, vous devez parcourir tour à tour tous les nœuds feuilles de l'arbre d'index B+ non clusterisé, la complexité temporelle est O(n) et vous devez revenir à la table après avoir trouvé l'enregistrement. n'est pas aussi bon que l'analyse complète de la table, donc l'optimiseur de requêtes choisit une analyse complète de la table.

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

Dans le cas d'un indice couvrant, l'indice « différent de » prend effet :

Indice de couverture, les deux champs à vérifier sont couverts par l'index conjoint, et les performances sont plus élevées. Bien qu'il soit toujours nécessaire de parcourir tour à tour tous les nœuds feuilles de l'arbre d'index B+ non clusterisé, la complexité temporelle est O(n), mais il n'est pas nécessaire de renvoyer la table, l'efficacité globale est plus élevée que sans l'index , et l'optimiseur de requêtes utilise à nouveau l'index.

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

8.0.2 Dans le cas d'un index couvrant, l'index de requête floue gauche prend effet

Dans le cas d'un index non couvrant, la requête floue de gauche provoque l'échec de l'index

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

Dans le cas d'un index couvrant, l'index de requête floue gauche prend effet

La raison principale est également que l'arbre d'index B+ non clusterisé traverse les nœuds feuilles sans revenir à la table, l'efficacité sera supérieure à celle de l'analyse complète de la table et l'optimiseur de requêtes choisit une solution à haute efficacité.

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

Tout ce qui précède utilise l'index déclaré, mais la situation suivante n'est pas le cas. La colonne de requête a encore plus d'ID de classe et le résultat est que l'index n'est pas utilisé :

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

8.2 Avantages et inconvénients de la couverture des index

avantage:

1. Eviter de revenir sur la table (requête secondaire d'indexation de la table Innodb)

Innodb est stocké dans l'ordre de l'index clusterisé. Pour lnnodb, l'index secondaire stocke les informations de clé primaire de la ligne dans le nœud feuille. Si vous utilisez l'index secondaire pour interroger des données, après avoir trouvé la valeur de clé correspondante, il est également Il est nécessaire d'effectuer une requête secondaire via la clé primaire pour obtenir les données dont nous avons réellement besoin.

Dans l'index de couverture, les données requises peuvent être obtenues dans la valeur clé de l'index secondaire, évitant ainsi l'interrogation secondaire de la clé primaire, réduisant les opérations d'E/S et améliorant l'efficacité des requêtes.

2. Les E/S aléatoires peuvent être transformées en E/S séquentielles pour accélérer l'efficacité des requêtes.

Étant donné que l'index de couverture est stocké dans l'ordre de la valeur clé, pour la recherche de plage intensive en E/S, les données I0 de chaque ligne sont lues à partir du disque de manière aléatoire. Les E/S de lecture se transforment en E/S séquentielles pour les recherches d'index.

Étant donné qu'un index de couverture peut réduire le nombre de recherches dans l'arborescence et améliorer considérablement les performances des requêtes, l'utilisation d'un index de couverture est une méthode courante d'optimisation des performances.

Désavantages:

Des questions spécifiques doivent être analysées en détail :

Il y a toujours un coût à maintenir des champs indexés. Par conséquent, il y a des compromis à prendre en compte lors de l’établissement du nombre d’index pour prendre en charge les index de couverture. C’est le travail du DBA d’entreprise, ou architecte de données d’entreprise.

9. Ajouter un index à une chaîne

9.1 Index des préfixes

Il existe une table enseignant, la définition de la table est la suivante :

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

Le conférencier doit se connecter avec une adresse e-mail, donc une déclaration similaire à celle-ci doit apparaître dans le code de l'entreprise :

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

S'il n'y a pas d'index dans le champ email, cette instruction ne peut effectuer qu'une analyse complète de la table .

MySQL prend en charge les index de préfixes. Par défaut, si vous créez un index sans spécifier de longueur de préfixe, l'index contiendra la chaîne entière.

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

Quelle est la différence entre ces deux définitions différentes en termes de structure et de stockage des données ? La figure ci-dessous est un diagramme schématique de ces deux index.

ainsi que

Si index1 est utilisé (l'index contient la chaîne entière), l'ordre d'exécution est le suivant :

  1. Recherchez l'enregistrement qui satisfait à la valeur d'index de « [email protected] » dans l'arborescence d'index de index1 et obtenez la valeur de ID2 ;
  2. Revenez au tableau pour trouver la ligne dont la valeur de clé primaire est ID2 sur la clé primaire, jugez que la valeur de l'e-mail est correcte et ajoutez cet enregistrement de ligne à l'ensemble de résultats ;
  3. Prenez l'enregistrement suivant à la position que vous venez de trouver dans l'arborescence d'index de index1 et constatez que la condition email='[email protected]' n'est plus satisfaite et la boucle se termine.

Dans ce processus, il n'est nécessaire de récupérer les données qu'une seule fois à partir de l'index de clé primaire, le système considère donc qu'une seule ligne a été analysée.

Si index2 est utilisé (l'index contient le préfixe de chaîne email(6)), la séquence d'exécution est la suivante :

  1. Recherchez l'enregistrement qui satisfait la valeur d'index de « zhangs » dans l'arborescence d'index index2, et le premier trouvé est ID1 ;
  2. Revenez au tableau et recherchez la ligne dont la valeur de clé primaire est ID1 sur la clé primaire, jugez que la valeur de l'e-mail n'est pas « [email protected] » et supprimez l'enregistrement dans cette ligne ;
  3. Prenez l'enregistrement suivant à l'emplacement que vous venez de trouver sur l'index2 et constatez qu'il s'agit toujours de « zhangs », supprimez ID2, puis revenez à la table pour récupérer la ligne entière de l'index ID, puis jugez que la valeur est correcte. cette fois, et ajoutez cette ligne au jeu de résultats ;
  4. Répétez l'étape précédente jusqu'à ce que la valeur obtenue sur index2 ne soit pas 'zhangs' , la boucle se termine.

C'est-à-dire que l'utilisation de l'index de préfixe et la définition de la longueur peuvent économiser de l'espace sans ajouter trop de coûts de requête supplémentaires. Le degré de discrimination a déjà été mentionné, et plus le degré de discrimination est élevé, mieux c'est . Car plus le degré de discrimination est élevé, moins il y a de valeurs clés en double.

9.2 L'index de préfixe ne peut pas utiliser l'index de couverture

Étant donné que les données trouvées par l'arborescence d'index non clusterisée sont le préfixe et l'identifiant, le préfixe n'est pas une donnée complète et il doit être renvoyé à l'arborescence d'index clusterisée.

Par conséquent, l’utilisation d’un index de préfixe n’a pas besoin d’optimiser les performances des requêtes de l’index de couverture, ce qui est également un facteur que vous devez prendre en compte lors du choix d’utiliser ou non un index de préfixe.

10. Déroulage de l'index

10.1 Présentation

Index Condition Pushdown (ICP, Index Condition Pushdown) est une nouvelle fonctionnalité de MySQL 5.6. Il s'agit d'une manière optimisée d'utiliser les index pour filtrer les données au niveau de la couche du moteur de stockage.

  • S'il n'y a pas d'ICP : lorsqu'un champ de l'index conjoint est une requête floue (non floue à gauche), une fois le champ jugé, les champs suivants ne peuvent pas être utilisés pour un jugement de condition direct, et le jugement doit être effectué après le retour à la table.
  • Après l'activation d'ICP : Lorsqu'un champ de l'index conjoint est une requête floue (non laissée floue), une fois le champ jugé, les champs suivants peuvent être jugés directement. Une fois le jugement filtré, revenez à la table pour vérifier le conditions des domaines non compris dans l'index commun juge. Le principal point d'optimisation est de filtrer avant de revenir à la table pour réduire le nombre de fois où il faut revenir à la table. Application principale : une requête floue (non floue à gauche) provoque le désordre des champs derrière le champ dans l'index et doit être jugée en revenant à la table. Cependant, si le refoulement d'index est utilisé, il n'est pas nécessaire de revenir à la table, et le jugement est directement dans l'arbre d'index commun.

S'il n'y a pas d'ICP , le moteur de stockage parcourra l'index pour localiser les lignes de la table de base et les renverra au serveur MySQL, et le serveur MySQL évaluera si les conditions derrière WHERE sont réservées.
Une fois ICP activé , si une partie de la condition WHERE peut être filtrée en utilisant uniquement les colonnes de l'index, le serveur MySQL placera cette partie de la condition WHERE dans le filtre du moteur de stockage. Le moteur de stockage filtre ensuite les données à l'aide des entrées d'index et lit les lignes de la table uniquement si cette condition est remplie.

Avantages : ICP peut réduire le nombre de fois où le moteur de stockage doit accéder à la table de base et le nombre de fois où le serveur MySQL doit accéder au moteur de stockage. Cependant, l'effet d'accélération d'ICP dépend de la proportion de données filtrées par ICP dans le moteur de stockage. 

Exemple:

Index conjoints qui ne prennent pas en charge le refoulement d'index : par exemple, index (nom, âge), nom de requête comme « z% » et age= ? , la requête floue provoque le désordre de l'âge. Lors de l'interrogation de l'arbre d'index commun, seul le nom est vérifié, et les âges suivants ne peuvent pas être directement jugés par la condition, et l'âge doit être jugé après le retour à la table.

Et l'index conjoint qui prend en charge le refoulement d'index : par exemple, l'index (nom, âge), le nom de la requête comme « z% » et l'âge et l'adresse, non seulement vérifient le nom lors de l'interrogation de l'arbre d'index commun, mais jugent également l'âge ultérieur, filtrer et renvoyer l'adresse de jugement de la table.

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;

Avantages :  dans certains scénarios, ICP peut réduire considérablement le nombre de retours de table et améliorer les performances. ICP peut réduire le nombre de fois où le moteur de stockage doit accéder à la table de base et le nombre de fois où le serveur MySQL doit accéder au moteur de stockage. Mais l'effet d'accélération d'ICP dépend de la proportion de données filtrées par ICP dans le moteur de stockage .

10.2 Conditions d'utilisation de l'ICP

  • Le type d'accès à la table est range, ref, eq_ref ou ref_or_null.
  • Moteur de stockage : ICP peut être utilisé pour les moteurs de stockage InnDB et MyISAM
  • Des index secondaires sont requis : pour les tables InnoDB, ICP n'est utilisé que pour les index secondaires. L'objectif d'ICP est de réduire le nombre de lectures de lignes complètes, réduisant ainsi les opérations d'E/S.
  • Ne doit pas être un index de couverture : lorsque SQL utilise un index de couverture, la méthode d'optimisation ICP n'est pas prise en charge. Parce que l'utilisation d'ICP dans ce cas ne réduira pas les E/S.
  • Les conditions pour les sous-requêtes corrélées ne peuvent pas utiliser ICP
  • Doit être la version 5.6 et supérieure : la version MySQL 5.6 est introduite et activée par défaut, et les versions précédentes ne prennent pas en charge le refoulement d'index.
  • Le champ Where doit être dans la colonne d'index : Toutes les conditions Where ne peuvent pas être filtrées par ICP. Si le champ de la condition Where n'est pas dans la colonne d'index, il est quand même nécessaire de lire les enregistrements de la table entière au serveur pour où le filtrage.

10.3 Activation/Désactivation du PCI

  • Le refoulement des conditions d’indexation est activé par défaut. Il peut être contrôlé en définissant la variable système optimiseur_switch : index_condition_pushdown
# 打开索引下推
SET optimizer_switch = 'index_condition_pushdown=on';

# 关闭索引下推
SET optimizer_switch = 'index_condition_pushdown=off';
  • Lorsque la condition d'index est abaissée, le contenu de la colonne Extra dans la sortie de l' instruction EXPLAIN s'affiche comme Using index condition .

10.4 Cas d'utilisation du PCI

  • Index de clé primaire (schéma simplifié)

Index secondaire zip_last_first (le schéma simplifié, les pages de données et autres informations sont omis ici)

10.5 Comparaison des performances entre l'activation et la désactivation d'ICP

11. Indice ordinaire vs indice unique

D'un point de vue performance, choisissez-vous un indice unique ou un indice normal ? Quelle est la base du choix ?

Supposons que nous ayons une table dont la clé primaire est ID. Il y a un champ k dans la table et il y a un index sur k, en supposant que les valeurs du champ k ne sont pas répétées.

L'instruction de création de table pour cette table est :

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

Les valeurs (ID,k) de R1~R5 dans le tableau sont respectivement (100,1), (200,2), (300,3), (500,5) et (600,6).

11.1 Performances approximatives des requêtes

Supposons que l'instruction pour exécuter la requête soit select id from test où k=5.

  • Pour un index normal, après avoir trouvé le premier enregistrement (5 500) qui satisfait à la condition, il est nécessaire de rechercher l'enregistrement suivant jusqu'à ce que le premier enregistrement qui ne remplisse pas la condition k=5 soit rencontré.
  • Pour un index unique, puisque l'index définit l'unicité, après avoir trouvé le premier enregistrement qui remplit la condition, la recherche s'arrêtera.

Alors, quel est l’écart de performance provoqué par cette différence ? La réponse est très peu .

11.2 Les performances de mise à jour de l'index ordinaire sont plus élevées, changez le tampon

Cache d'écriture (tampon de changement) :

Lorsqu'une page de données doit être mise à jour, si la page de données est en mémoire, elle sera mise à jour directement, et si la page de données n'est pas en mémoire, InooDB mettra en cache ces opérations de mise à jour dans le tampon de modification sans affecter la cohérence des données que cette page de données n'a pas besoin d'être lue à partir du disque. Lorsque la requête suivante doit accéder à cette page de données, lisez la page de données dans la mémoire, puis exécutez les opérations liées à cette page dans le tampon de modification. De cette façon, l’exactitude de la logique des données peut être garantie.

fusion : le processus d'application de l'opération dans le tampon de modification à la page de données d'origine pour obtenir le dernier résultat est appelé fusion. En plus d'accéder à cette page de données déclenchera la fusion, le système dispose d'un fil d'arrière-plan qui fusionnera périodiquement. L'opération de fusion est également effectuée lors d'un arrêt normal de la base de données.

Si l'opération de mise à jour peut d'abord être enregistrée dans le tampon de modification pour réduire les lectures sur le disque , la vitesse d'exécution de l'instruction sera considérablement améliorée. De plus, la lecture des données en mémoire nécessite le pool de mémoire tampon, cette méthode peut donc également éviter d'occuper la mémoire et améliorer l'utilisation de la mémoire.

La mise à jour de l'index unique ne peut pas utiliser le tampon de modification , en fait, seuls les index ordinaires peuvent être utilisés.

Faites une distinction :

  • Lire les données à l'aide du pool tampon pool tampon ;
  • Le journal redo a un tampon de journalisation , qui consiste à écrire les données mises à jour dans le pool de tampons dans le tampon de journalisation. Lorsque la transaction est validée, le tampon de journalisation est vidé dans le fichier de journalisation ou le cache de page en fonction du vidage stratégie.

11.3 Utiliser des scénarios de tampon de changement

  • Comment choisir un index ordinaire et un index unique ? En fait, il n'y a aucune différence dans les capacités de requête entre ces deux types d'index.La principale considération est l'impact sur les performances de mise à jour . Il est donc recommandé d' essayer de choisir un index commun .

  • En utilisation réelle, on constatera que l'utilisation combinée d'index ordinaires et de tampons de modification est très évidente pour mettre à jour et optimiser des tables contenant de grandes quantités de données .

  • Ne convient pas aux situations de tampon de modification : si toutes les mises à jour sont immédiatement suivies de requêtes sur cet enregistrement, vous devez alors désactiver le tampon de modification. Dans d'autres cas, le tampon de modification peut améliorer les performances de mise à jour.

  • Lorsque la transaction est validée, l'opération de tampon de modification sera également enregistrée dans le journal redo , donc lorsque le crash est récupéré, le tampon de modification peut également être récupéré.

  • Étant donné que l'index unique n'utilise pas le mécanisme d'optimisation du tampon de modification, si l'activité est acceptable, il est recommandé de donner la priorité aux index non uniques du point de vue des performances. Mais si « l’activité n’est pas garantie », comment y faire face ?

    • Premièrement, l’exactitude commerciale est prioritaire. Notre principe est que « le code métier a la garantie de ne pas écrire de données en double » pour discuter des problèmes de performances. Si l'activité ne peut pas être garantie, ou si l'entreprise exige que la base de données soit une contrainte, alors il n'y a pas d'autre choix que de créer un index unique. Dans ce cas, l'intérêt de cette section est de vous fournir une idée de dépannage supplémentaire si une grande quantité de données est insérée lentement et que le taux d'accès à la mémoire est faible.
    • Ensuite, dans certains scénarios de « bibliothèque d’archives », vous pouvez envisager d’utiliser des index uniques. Par exemple, les données en ligne ne doivent être conservées que pendant six mois, puis les données historiques sont stockées dans la bibliothèque d'archives. À ce stade, l'archivage des données garantit déjà qu'il n'y a pas de conflits de clés uniques. Pour améliorer l'efficacité de l'archivage, vous pouvez envisager de remplacer l'index unique de la table par un index commun.

12. Optimisation SQL

12.1 Différence entre EXISTE et IN

question:

Je ne comprends pas très bien quelle situation doit utiliser EXISTS et quelle situation doit utiliser IN. Le critère de sélection est de voir si l'index du tableau peut être utilisé ?

répondre:

12.2 Recommander COUNT(*) ou COUNT(1)

Utilisez COUNT(1), COUNT(*) autant que possible pour compter le nombre de lignes : lorsque COUNT(1), COUNT(*), l'optimiseur de requêtes donnera la priorité à la sélection de l'arbre d'index secondaire avec des index et occupera le plus petit espace pour les statistiques. Les statistiques des arbres d'index clusterisés sont utilisées lors de l'accès aux arbres d'index non clusterisés, ce qui prend beaucoup de place. Bien sûr, COUNT (champ d'index secondaire d'espace minimum) peut également être utilisé, mais le problème n'est pas aussi grave que la sélection automatique par l'optimiseur.

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

 Question : Il existe trois façons de compter le nombre de lignes dans une table de données dans MySQL : SELECT COUNT(*), SELECT COUNT(1) et SELECT COUNT (champs spécifiques). Quelle est l'efficacité des requêtes entre ces trois méthodes ?

Réponse : Si vous souhaitez compter le nombre de lignes de données non nulles dans un certain champ, c'est une autre affaire. Après tout, le principe de la comparaison de l'efficacité d'exécution est que les résultats sont les mêmes.

COUNT(*) et COUNT(1) : COUNT(*) et COUNT(1) exécutent tous deux COUNT(*) sur tous les résultats , et il n'y a essentiellement aucune différence entre COUNT(*) et COUNT(1) (le temps d'exécution de les deux peuvent être il y a une légère différence, mais vous pouvez toujours considérer l'efficacité d'exécution des deux comme égale). S'il y a une clause WHERE, elle comptera toutes les lignes de données qui répondent aux conditions de filtrage. S'il n'y a pas de clause WHERE, elle comptera le nombre de lignes de données dans la table de données.

Les statistiques MylSAM n'ont besoin que de O(1) : s'il s'agit du moteur de stockage MylSAM, le nombre de lignes dans la table de données statistiques n'a besoin que de la complexité de O(1) , car chaque table de données MyISAM contient des méta-informations pour stocker la valeur row_count . , La cohérence est garantie par des verrous au niveau de la table. S'il s'agit d'un moteur de stockage InnoDB, car innoDB prend en charge les transactions et utilise des verrous au niveau des lignes et le mécanisme MVCC, il ne peut pas maintenir une variable row_count comme MyISAM, il doit donc analyser la table entière, qui est de complexité O(n) , et boucle+ Le comptage se fait en comptant.

Suggestion de sélection : Dans ImnoDB, si vous utilisez COUNT (champ spécifique) pour compter le nombre de lignes de données, essayez d' utiliser des index secondaires . Étant donné que la clé primaire est un index clusterisé et que les nœuds feuilles de l'index cluster contiennent l'intégralité de l'enregistrement, la quantité de données à charger dans la mémoire pendant les statistiques est plus importante et les performances sont moins bonnes. Pour COUNT(*) et COUNT(1), ils n'ont pas besoin de rechercher des lignes spécifiques, mais comptent uniquement le nombre de lignes, et le système utilisera automatiquement l'index secondaire qui occupe moins d'espace pour les statistiques . S'il existe plusieurs index secondaires, l'index secondaire avec le plus petit key_len sera utilisé pour l'analyse. Lorsqu'il n'y a pas d'index secondaire, l'index de clé primaire sera utilisé pour les statistiques.

12.3 Suggérer SELECT(field) au lieu de SELECT(*)

Dans la requête de table, il est recommandé de spécifier les champs, de ne pas utiliser * comme liste de champs de la requête, il est recommandé d'utiliser la requête SELECT <field list>. raison:

① Pendant le processus d'analyse, MySQL interrogera le dictionnaire de données pour convertir "*" en tous les noms de colonnes dans l'ordre , ce qui consommera beaucoup de ressources et de temps.

② L'index de couverture ne peut pas être utilisé

12.4 Effet de LIMIT 1 sur l'optimisation

Il est destiné aux instructions SQL qui analysent la table entière . Si vous pouvez être sûr qu'il n'y a qu'un seul jeu de résultats , lors de l'ajout de LIMIT 1, l'analyse ne continuera pas lorsqu'un résultat est trouvé, ce qui accélérera la requête.

Si la table de données a établi un index unique pour le champ, vous pouvez effectuer une requête via l'index. Si vous n'analysez pas la table entière, vous n'avez pas besoin d'ajouter LIMIT 1.

12.5 Utiliser COMMIT plus souvent

Dans la mesure du possible, utilisez COMMIT autant que possible dans votre programme, afin que les performances du programme soient améliorées et que la demande soit réduite en raison des ressources libérées par COMMIT.

Ressources publiées par COMMIT :

  • Informations utilisées pour restaurer les données sur le segment de restauration
  • verrous acquis par les instructions de programme
  • Espace dans le tampon du journal de rétablissement/annulation
  • Gérer les dépenses internes dans les 3 ressources ci-dessus

13. Idées de conception clés primaires

Parlons d’une question pratique : Comment est conçue la clé primaire de la base de données Taobao ?

Certaines réponses fausses et scandaleuses circulent encore sur Internet année après année et deviennent même ce qu'on appelle les réglementations militaires MySQL. Parmi elles, l’une des erreurs les plus évidentes concerne la conception de la clé primaire de MySQL.

Les réponses de la plupart des gens sont si sûres : utilisez BIGINT sur 8 octets comme clé primaire au lieu de INT. Faux !

Une telle réponse se situe uniquement au niveau de la base de données, sans penser à la clé primaire d'un point de vue commercial . La clé primaire est-elle un identifiant à incrémentation automatique ? À l'heure actuelle, l'utilisation de l'auto-incrémentation comme clé primaire peut même ne pas réussir la conception architecturale .

13.1 Inconvénients de la clé primaire à incrémentation automatique

L'ID d'incrémentation automatique est utilisé comme clé primaire, ce qui est facile à comprendre. Presque toutes les bases de données prennent en charge le type d'incrémentation automatique, mais l'implémentation est différente. En plus d'être simples, les identifiants auto-incrémentés présentent des inconvénients. De manière générale, il existe les problèmes suivants :

  • faible fiabilité

    Il y a un problème avec le backtracking d'ID par incrémentation automatique, qui n'a été résolu que dans la dernière version de MySQL 8.0.

    Problème de retour en arrière : par exemple, insérez dans une nouvelle table trois lignes de données dont les clés primaires sont 1, 2 et 3. À ce stade, utilisez la SHOW CREATE TABLEcommande pour vérifier que la valeur de la table AUTO_INCREMENTest 4, ce qui ne pose aucun problème.

    Supprimez ensuite la ligne de données avec ID=3 et AUTO_INCREMENTla valeur interrogée à nouveau est toujours 4, ce qui ne pose aucun problème.

    Mais si vous redémarrez MySQL, cette valeur reviendra à 3 au lieu de 4, et un retour en arrière se produira.

  • faible sécurité

    L'interface exposée permet de deviner très facilement les informations correspondantes . Par exemple, une interface telle que /User/1/ peut facilement deviner la valeur de l'ID utilisateur et le nombre total d'utilisateurs, et peut également facilement explorer les données via l'interface.

  • mauvaise performance

    L'ID d'incrémentation automatique a des performances médiocres et doit être généré côté serveur de base de données.

  • Des fonctions d'exécution supplémentaires sont nécessaires pour connaître la valeur d'auto-incrémentation, ce qui affecte les performances

    L'entreprise doit également exécuter une fonction similaire à last_insert_id() pour connaître la valeur d'auto-incrémentation qui vient d'être insérée, ce qui nécessite une interaction réseau supplémentaire. Dans un système massivement concurrent, une instruction SQL supplémentaire signifie une surcharge de performances supplémentaire .

  • Le monde n’est pas unique, la concurrence de verrouillage auto-intensifiée affecte les performances en cas de concurrence élevée

    Le point le plus important est que l'ID d'auto-incrémentation est unique localement, unique uniquement dans l'instance de base de données actuelle, non unique au niveau mondial et unique parmi n'importe quel serveur. Pour les systèmes distribués actuels, c’est tout simplement un cauchemar.

  • L'incrémentation automatique n'est plus applicable lorsque la sous-base de données et la table sont migrées.

13.2 Essayez de ne pas utiliser les champs métier comme clés primaires

Afin d'identifier de manière unique les informations d'un membre, une clé primaire doit être définie pour la table d'informations sur le membre. Alors, comment définir la clé primaire de cette table pour atteindre notre objectif idéal ? Nous considérons ici le domaine métier comme clé primaire.

Les données du tableau sont les suivantes :

Dans ce tableau, quel champ est le plus approprié ?

  • Sélectionnez le numéro de carte (cardno)

Le numéro de carte de membre (cardno) semble plus approprié, car le numéro de carte de membre ne peut pas être vide et est unique, ce qui peut être utilisé pour identifier un dossier de membre.

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)

Différents numéros de carte de membre correspondent à différents membres, et le champ « cardno » identifie de manière unique un certain membre. Si tel est le cas, le numéro de carte de membre correspond aux membres un par un et le système peut fonctionner normalement.

Mais dans la réalité, le numéro de carte de membre peut être réutilisé . Par exemple, Zhang San a quitté son adresse d'origine en raison d'un changement d'emploi et ne se rend plus au magasin du commerçant pour consommer (la carte de membre a été restituée), donc Zhang San n'est plus membre du magasin du commerçant. Cependant, le commerçant ne voulait pas que la carte de membre soit vide, il a donc envoyé la carte de membre avec le numéro de carte « 10000001 » à Wang Wu.

Du point de vue de la conception du système, ce changement modifie uniquement les informations du membre dont le numéro de carte est « 10000001 » dans le tableau d'informations du membre, et n'affectera pas la cohérence des données. C'est-à-dire que si vous modifiez les informations du membre dont le numéro de carte de membre est "10000001", chaque module du système obtiendra les informations du membre modifiées, et il n'y aura pas "certains modules obtiendront les informations du membre avant modification, et certains modules obtenir les informations modifiées sur les membres ultérieurs, ce qui entraîne une incohérence des données au sein du système". Il n’y a donc aucun problème au niveau du système d’information.
Mais du point de vue commercial, l'utilisation du système pose de gros problèmes qui affecteront les commerçants.

Par exemple, nous avons un tableau des flux de ventes (trans), qui enregistre tous les détails des flux de ventes. Le 1er décembre 2020, Zhang San a acheté un livre au magasin et a dépensé 89 yuans. Ensuite, il y a un enregistrement de Zhang San achetant des livres dans le système, comme indiqué ci-dessous :

Vérifions ensuite les records de ventes d'adhésions au 1er décembre 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 la carte de membre « 10 000 001 » est à nouveau délivrée à Wang Wu, nous modifierons le formulaire d'information d'adhésion. Lors d'une requête :

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)

Le résultat obtenu cette fois est le suivant : Wang Wu a acheté un livre le 1er décembre 2020 et a dépensé 89 yuans. Visiblement faux ! Conclusion : N'utilisez pas le numéro de carte de membre comme clé primaire.

  • Sélectionnez le numéro de téléphone ou le numéro d'identification du membre

Un numéro de téléphone de membre peut-il être utilisé comme clé primaire ? Certainement pas. En fonctionnement réel, le numéro de téléphone mobile est également repris par l'opérateur et réémis à d'autres.

Et le numéro d'identification ? Cela semble possible. Étant donné que la carte d'identité ne sera jamais répétée, il existe une correspondance biunivoque entre le numéro d'identification et une personne. Mais le problème est que le numéro d’identification relève de la vie privée et que les clients ne voudront peut-être pas vous le donner. S’il est obligatoire pour les membres d’enregistrer leur numéro d’identification, de nombreux clients seront chassés. En fait, le téléphone du client présente également ce problème, c'est pourquoi nous autorisons que le numéro d'identification et le numéro de téléphone soient vides lors de la conception du formulaire d'information du membre.

Par conséquent, il est recommandé de ne pas utiliser de champs professionnels comme clés primaires . Après tout, en tant que techniciens de conception de projets, aucun d'entre nous ne peut prédire quel domaine d'activité sera répété ou réutilisé en raison des exigences commerciales du projet tout au long du cycle de vie du projet.

Expérience : Lorsque vous commencez à utiliser MySQL, de nombreuses personnes ont tendance à commettre l'erreur d'utiliser des champs métier comme clés primaires. Ils tiennent pour acquis qu'ils comprennent les besoins de l'entreprise, mais la situation réelle est souvent inattendue et le coût du changement le paramètre de clé primaire est très élevé .

13.3 Conception de la clé primaire du numéro de commande Taobao

Dans le secteur du commerce électronique de Taobao, le service de commande est une activité essentielle. Excusez-moi, comment la clé primaire Taobao est-elle conçue dans le tableau de commande ? Est-ce un identifiant à incrémentation automatique ?

Ouvrez Taobao et regardez les informations de commande :

Comme le montre la figure ci-dessus, le numéro de commande n'est pas un identifiant à incrémentation automatique ! Examinons en détail les 4 numéros de commande ci-dessus :

1550672064762308113
1481195847180308113
1431156171142308113
1431146631521308113

Le numéro de commande comporte 19 chiffres et les 5 derniers chiffres de la commande sont tous identiques, 08113. Et les 14 premiers chiffres du numéro de commande augmentent de manière monotone.

Devinez hardiment, la conception de l'ID de commande de Taobao devrait être :

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

Une telle conception peut être unique au monde et extrêmement conviviale pour les requêtes de systèmes distribués.

13.4 Conception de clé primaire recommandée

13.4.1 Sélection de la stratégie clé principale des activités principales et non essentielles

Activité non essentielle : ID d'auto-incrémentation de clé primaire de la table correspondante, telle que les alarmes, les journaux, la surveillance et d'autres informations.

Activité principale  : La conception de la clé primaire doit au moins être globalement unique et croissante de manière monotone. L'unicité globale est garantie d'être unique entre chaque système, et l'augmentation monotone consiste à espérer que l'insertion n'affectera pas les performances de la base de données. Il est recommandé d'utiliser MySQL8.0 pour le transformer en UUID ordonné. Plus précisément, utilisez la fonction uuid_to_bin (@uuid,true) pour convertir l'UUID en UUID ordonné.

13.4.2  Caractéristiques de l'UUID

La conception de clé primaire la plus simple est recommandée ici : UUID.

Globalement unique , occupant 36 octets, les données sont dans le désordre et les performances d'insertion sont médiocres.

Reconnaître les UUID :

  • Pourquoi les UUID sont-ils uniques au monde ?
  • Pourquoi l’UUID prend 36 octets ?
  • Pourquoi les UUID ne sont-ils pas ordonnés ?

La composition UUID de la base de données MySQL est la suivante :

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

Prenons comme exemple la valeur UUID e0ea12d4-6473-11eb-943c-00155dbaa39d :

Pourquoi les UUID sont-ils uniques au monde ? 

La partie temporelle de l'UUID occupe 60 bits et l'horodatage stocké est similaire à TIMESTAMP, mais il représente le décompte de 100 ns de 1582-10-15 00:00:00.00 à aujourd'hui. On peut voir que la précision temporelle du stockage UUID est supérieure à celle de TIMESTAMPE et que la probabilité de duplication dans la dimension temporelle est réduite à 1/100ns .

La séquence d'horloge vise à éviter la possibilité que l'horloge soit rappelée et provoque une duplication de l'heure . L'adresse MAC est utilisée pour l'unicité globale .

Pourquoi l’UUID prend 36 octets ?

Les UUID sont stockés sous forme de chaînes et sont conçus avec des chaînes "-" inutiles, donc un total de 36 octets est requis.

Pourquoi les UUID sont-ils aléatoires et non ordonnés ?

Parce que dans la conception de l'UUID, le bit de temps le plus bas est placé au premier plan et les données de cette partie sont constamment changeantes et hors d'usage.

13.4.3 Schéma de clé primaire MySQL 8.0 : UUID ordonnés

Transformation en ordre : si les bits haut et bas du temps sont échangés, le temps augmente de manière monotone et il devient croissant de manière monotone. MySQL 8.0 peut remplacer la méthode de stockage à faible temps et à temps élevé, de sorte que l'UUID soit un UUID ordonné.

Optimiser l'occupation de l'espace : MySQL 8.0 résout également le problème d'occupation de l'espace de l'UUID, supprime la chaîne "-" dénuée de sens dans la chaîne UUID et enregistre la chaîne en type binaire, réduisant ainsi l'espace de stockage à 16 octets.

Les fonctions ci-dessus peuvent être réalisées via la fonction uuid_to_bin fournie par MySQL8.0 . De même, MySQL fournit également la fonction bin_to_uuid pour la conversion :

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

L'UUID est converti en un UUID ordonné par la fonction uuid_to_bin (@uuid,true) . Globalement unique + augmentation monotone , n'est-ce pas la clé primaire que nous voulons !

Test de performances UUID commandé :

Comment l'UUID ordonné sur 16 octets se compare-t-il à l'ID auto-incrémenté précédent de 8 octets en termes de performances et d'espace de stockage ?

Faisons un test, insérons 100 millions de données, chaque donnée occupe 500 octets, et contient 3 index secondaires. Le résultat final est le suivant :

À partir de la figure ci-dessus, nous pouvons voir qu'il est le plus rapide d'insérer 100 millions d'UUID commandés par données, et que dans une utilisation professionnelle réelle, les UUID ordonnés peuvent être générés du côté commercial . Il est également possible de réduire davantage le nombre d'interactions SQL.

De plus, bien que l'UUID commandé contienne 8 octets de plus que l'ID auto-incrémenté, il ne fait qu'augmenter l'espace de stockage de la 3G, ce qui est acceptable.

Dans l'environnement Internet actuel, la conception d'une base de données avec un ID auto-incrémenté comme clé primaire n'est pas recommandée. Une implémentation unique au monde comme l'UUID ordonné est plus recommandée.

De plus, dans un système d'entreprise réel, la clé primaire peut également être ajoutée aux attributs de l'entreprise et du système, tels que le numéro de téléphone de l'utilisateur, les informations de la salle informatique, etc. Une telle conception de clé primaire mettra encore plus à l'épreuve le niveau de l'architecte.

13.4.4 Schéma de clé primaire avant MySQL8.0 : affectation manuelle

Attribuez manuellement le champ comme clé primaire !

Par exemple, concevez la clé primaire de la table d'appartenance de chaque branche, car si les données générées par chaque machine doivent être fusionnées, le problème de duplication de clé primaire peut survenir.

Vous pouvez avoir un tableau d'informations de gestion dans la base de données MySQL du siège social et ajouter un champ à ce tableau pour enregistrer la valeur maximale du numéro de membre actuel.

Lors de l'ajout d'un membre, le magasin obtient d'abord la valeur maximale de la base de données MySQL du siège social, ajoute 1 à cette base, puis utilise cette valeur comme "id" du nouveau membre, et en même temps, met à jour l'actuel. membre dans le tableau d'informations de gestion de la base de données MySQL du siège La valeur maximale du nombre.

De cette façon, lorsque chaque magasin ajoute des membres, il opère sur les champs de la table de données dans la même base de données MySQL du siège social, ce qui résout le problème des conflits de numéros de membre lorsque chaque magasin ajoute des membres.

13.3.5 Algorithme de flocon de neige

Identifiants commandés.

Un entier de 64 bits du type de données Long : composé d'un bit de signe de 1 bit, d'un horodatage de 41 bits, d'un identifiant de machine de travail de 10 bits et d'un numéro de série de 12 bits.

avantage:

  • Ordonné : tous les identifiants générés sont incrémentés en fonction de la tendance temporelle
  • Distribué et non répétitif : aucun identifiant en double ne sera généré dans l'ensemble du système distribué.

défaut:

  • S'appuyer sur l'horloge de la machine : en s'appuyant sur l'horloge de la machine, si l'horloge de la machine est rappelée, des identifiants en double seront générés.
  • Les horloges distribuées non synchronisées entraînent un échec d'incrémentation : incrémentation sur une seule machine, mais si dans un environnement distribué, les horloges de chaque machine peuvent ne pas être synchronisées, il se peut qu'il ne s'agisse pas d'un incrément global.
  • Perte de précision : les nombres binaires 64 bits comportent généralement 19 chiffres lorsqu'ils sont stockés en décimal, mais le front-end js ne peut garantir l'exactitude que des 16 premiers chiffres. Lorsque le front-end obtient ces données, il arrondira les trois derniers. chiffres. La précision est perdue.

Je suppose que tu aimes

Origine blog.csdn.net/qq_40991313/article/details/130804019
conseillé
Classement