Lorsque le décalage de limite est relativement important, utilisez une sous-requête pour optimiser SQL et réduire les opérations de retour à la table

Lorsque le décalage de limite est relativement important, par exemple,
prenez 5 éléments de données de l'offre = 30000
sélectionnez * de t où a = limite '11' 30000,5

Il existe une table de flux financiers sans base de données ni table. Le volume de données actuel est 9555695. La limite est utilisée pour la requête de pagination. La requête avant l'optimisation prend 16 s 938 ms (exécution: 16 s 831 ms, extraction: 107 ms), selon après avoir ajusté le SQL de la manière suivante, il a fallu 347 ms (exécution: 163 ms, récupération: 184 ms);

Opération: placez les conditions de requête dans la sous-requête. La sous-requête vérifie uniquement l'ID de clé primaire, puis utilise la clé primaire déterminée dans la sous-requête pour interroger d'autres champs d'attribut
Principe: réduire les opérations de la table arrière;

Je n'ai jamais pensé au principe spécifique auparavant, maintenant je vais l'ajouter, car dans la sous-requête, sélectionnez l'id, où est l'index, la valeur correspondant à la clé d'index est exactement la valeur de la clé primaire (ici l'id par défaut est la clé primaire value), il n'est donc pas nécessaire de revenir à la table, c'est-à-dire de vérifier à nouveau la table en fonction de l'id, de sorte que certaines données seront sauvegardées dans le cache sous-jacent de mysql.

MySQL est généralement une structure d'index clusterisé de B + Tree. Selon la clé primaire (généralement id), les données requises peuvent être trouvées directement sur le nœud feuille, sans retourner à la table à interroger, c'est très rapide.

La combinaison des deux évite en fait l'étape de revenir à la requête de table basée sur la valeur de clé primaire correspondant à la valeur d'index

- SQL avant optimisation

SELECT  各种字段
FROM`table_name`
WHERE 各种条件
LIMIT 0,10;

-SQL optimisé

SELECT  各种字段
FROM`table_name` main_tale
RIGHT JOIN
(
SELECT  子查询只查主键
FROM`table_name`
WHERE 各种条件
LIMIT 0,10;
) temp_table ON temp_table.主键 = main_table.主键

Expliquez d'abord la version de MySQL:

mysql> selectversion();
+-----------+
| version() |
+-----------+
| 5.7.17    |
+-----------+
1 row in set (0.00 sec)

Structure de la table:

mysql> desc test;
+--------+---------------------+------+-----+---------+----------------+
| Field  | Type                | Null | Key | Default | Extra          |
+--------+---------------------+------+-----+---------+----------------+
| id     | bigint(20) unsigned | NO   | PRI | NULL    | auto_increment |
| val    | int(10) unsigned    | NO   | MUL | 0       |                |
| source | int(10) unsigned    | NO   |     | 0       |                |
+--------+---------------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)

id est une clé primaire auto-incrémentée et val est un index non unique.

Injectez une grande quantité de données, un total de 5 millions:

mysql> selectcount(*) fromtest;
+----------+
| count(*) |
+----------+
|  5242882 |
+----------+
1 row in set (4.25 sec)

Nous savons que lorsque le décalage dans les lignes de décalage limite est très important, il y aura des problèmes d'efficacité:

mysql> select * fromtestwhere val=4limit300000,5;
+---------+-----+--------+
| id      | val | source |
+---------+-----+--------+
| 3327622 |   4 |      4 |
| 3327632 |   4 |      4 |
| 3327642 |   4 |      4 |
| 3327652 |   4 |      4 |
| 3327662 |   4 |      4 |
+---------+-----+--------+
5 rows in set (15.98 sec)

Afin d'atteindre le même objectif, nous réécrirons généralement la déclaration suivante:

mysql> select * fromtest a innerjoin (selectidfromtestwhere val=4limit300000,5) b on a.id=b.id;
+---------+-----+--------+---------+
| id      | val | source | id      |
+---------+-----+--------+---------+
| 3327622 |   4 |      4 | 3327622 |
| 3327632 |   4 |      4 | 3327632 |
| 3327642 |   4 |      4 | 3327642 |
| 3327652 |   4 |      4 | 3327652 |
| 3327662 |   4 |      4 | 3327662 |
+---------+-----+--------+---------+
5 rows in set (0.38 sec)

Le décalage horaire est évident.

Pourquoi le résultat ci-dessus apparaît-il? Jetons un coup d'œil au processus de requête de select * from test où val = 4 limit 300000,5 ;:

Interrogez les données du nœud feuille d'index. Selon la valeur de clé primaire sur le nœud feuille, toutes les valeurs de champ nécessaires à interroger sur l'index clusterisé sont utilisées.

Similaire à l'image ci-dessous:

Insérez la description de l'image ici
Comme ci-dessus, vous devez interroger 300005 nœuds d'index, interroger 300005 données d'index en cluster, puis filtrer les résultats des 300 000 premiers éléments et retirer les 5 derniers éléments. MySQL consomme beaucoup d'E / S aléatoires pour interroger les données de l'index clusterisé, et les données de 300 000 requêtes d'E / S aléatoires n'apparaîtront pas dans le jeu de résultats.

Quelqu'un va certainement demander: Puisque l'index est utilisé au début, pourquoi ne pas d'abord interroger les 5 derniers nœuds requis le long des nœuds feuille d'index, puis interroger les données réelles dans l'index clusterisé. Cela ne nécessite que 5 E / S aléatoires, similaires au processus illustré ci-dessous:

Insérez la description de l'image ici

Confirmez que select * from test où val = 4 limit 300000, 5 is to scan 300005 index nodes and 300005 clustered index data nodes

Afin de vérifier que select * from test où val = 4 limit 300000,5 scanne 300005 nœuds d'index et 300005 nœuds de données d'index clusterisés, nous devons savoir si MySQL a un moyen de compter les nœuds de données dans une requête SQL via la fréquence des nœuds d'index. J'ai d'abord essayé la série Handler_read_ *, mais malheureusement aucune des variables ne peut remplir les conditions.

Je ne peux que confirmer indirectement:

Il y a un pool tampon dans InnoDB. Certaines pages de données ont été consultées récemment, notamment des pages de données et des pages d'index. Nous devons donc exécuter deux SQL pour comparer le nombre de pages de données dans le pool de mémoire tampon. Le résultat de la prédiction est d'exécuter select * from test a inner join (select id from test where val = 4 limit 300000,5); Après cela, le nombre de pages de données dans le pool de tampons est bien inférieur à select * from test où val = 4 limite 300000, 5; Le nombre correspondant, car le SQL précédent n'accède qu'à la page de données 5 fois, et ce dernier SQL accède à la page de données 300005 fois.

select * fromtestwhere val=4limit300000,5
mysql> select index_name,count(*) from information_schema.INNODB_BUFFER_PAGE where INDEX_NAME in('val','primary') and TABLE_NAME like'%test%'groupby index_name;Empty set (0.04 sec)

On peut voir qu'il n'y a actuellement aucune page de données sur la table de test dans le pool de mémoire tampon.

mysql> select * fromtestwhere val=4limit300000,5;
+---------+-----+--------+
| id      | val | source |
+---------+-----+--------+|
3327622 |   4 |      4 |
| 3327632 |   4 |      4 |
| 3327642 |   4 |      4 |
| 3327652 |   4 |      4 |
| 3327662 |   4 |      4 |
+---------+-----+--------+
5 rows in set (26.19 sec)
mysql> select index_name,count(*) from information_schema.INNODB_BUFFER_PAGE where INDEX_NAME in('val','primary') and TABLE_NAME like'%test%'groupby index_name;
+------------+----------+
| index_name | count(*) |
+------------+----------+
| PRIMARY    |     4098 |
| val        |      208 |
+------------+----------+2 rows in set (0.04 sec)

On peut voir qu'il y a 4 098 pages de données et 208 pages d'index pour la table de test dans le pool de mémoire tampon à ce moment.

select * from test a inner join (select id from test where val = 4 limit 300000,5); Afin d'éviter l'impact du dernier test, nous devons effacer le pool de tampons et redémarrer mysql.

mysqladmin shutdown
/ usr / local / bin / mysqld_safe &
mysql> sélectionnez nom_index, count (*) dans information_schema.INNODB_BUFFER_PAGE où INDEX_NAME in ('val', 'primary') et TABLE_NAME comme '% test%' groupby index_name;

Ensemble vide (0,03 sec) pour
exécuter SQL:

mysql> select * fromtest a innerjoin (selectidfromtestwhere val=4limit300000,5) b on a.id=b.id;
+---------+-----+--------+---------+
| id      | val | source | id      |
+---------+-----+--------+---------+
| 3327622 |   4 |      4 | 3327622 |
| 3327632 |   4 |      4 | 3327632 |
| 3327642 |   4 |      4 | 3327642 |
| 3327652 |   4 |      4 | 3327652 |
| 3327662 |   4 |      4 | 3327662 |
+---------+-----+--------+---------+
5 rows in set (0.09 sec)
mysql> select index_name,count(*) from information_schema.INNODB_BUFFER_PAGE where INDEX_NAME in('val','primary') and TABLE_NAME like'%test%'groupby index_name;
+------------+----------+
| index_name | count(*) |
+------------+----------+
| PRIMARY    |        5 |
| val        |      390 |
+------------+----------+
2 rows in set (0.03 sec)

Nous pouvons voir la différence évidente entre les deux: le premier SQL charge 4098 pages de données dans le pool de tampons, tandis que le second SQL ne charge que 5 pages de données dans le pool de tampons. Conformément à nos prévisions. Cela a également prouvé pourquoi le premier SQL était lent: lire un grand nombre de lignes de données inutiles (300 000), et finalement les rejeter. Et cela posera un problème: charger beaucoup de pages de données qui ne sont pas très chaudes dans le pool de tampons provoquera la pollution du pool de tampons et occupera l'espace du pool de tampons. Les problèmes rencontrés

Afin de garantir que le pool de mémoire tampon est effacé à chaque redémarrage, nous devons désactiver innodb_buffer_pool_dump_at_shutdown et innodb_buffer_pool_load_at_startup. Ces deux options peuvent contrôler le vidage des données dans le pool de mémoire tampon lorsque la base de données est arrêtée et les données dans le pool de mémoire tampon de sauvegarde chargé sur le disque lorsque la base de données est activée.

Je suppose que tu aimes

Origine blog.csdn.net/nmjhehe/article/details/113784048
conseillé
Classement