Interrogez généralement des requêtes lentes SQL
语句时会使用EXPLAIN
命令来查看SQL
语句的执行计划,通过返回的信息,可以了解到Mysql
优化器是如何执行SQL
, et l'analyse peut nous aider à fournir des idées d'optimisation.
La commande expliquer est principalement utilisée pour afficher le plan d'exécution des instructions SQL. Cette commande peut simuler l'exécution des instructions de requête SQL par l'optimiseur et peut nous aider à écrire et à optimiser SQL. Alors, quelles informations spécifiques peuvent expliquer pour nous aider à optimiser SQL ?
Ordre de lecture des tables
Type d'opération de lecture des données
Quels index peuvent être utilisés
Quels index sont réellement utilisés
Références entre les tables
Combien de lignes dans chaque table est interrogée par l'optimiseur
Utilisation : explain + sql à exécuter
Explain renverra une liste de plans d'exécution pour le SQL à exécuter. , la liste contient 12 champs, qui décrivent ensemble la manière dont SQL sera exécuté dans le plan d'exécution. La liste suivante décrit en détail la signification des champs de la table du plan d'exécution :
Nom du champ | Description |
---|---|
id | Le numéro de séquence de la requête d'instruction select, qui détermine l'ordre de lecture. de la table |
select_type | Le type de requête, c'est-à-dire le type d'opération de lecture des données |
table | Le nom de la table de la requête |
partitions | Partitions de table |
type | Type d'accès |
possible_keys | Index utilisables. S'il existe un index sur le champ impliqué dans la requête, l'index sera répertorié, mais il ne pourra pas être réellement utilisé par la requête. Si ce champ est nul mais que la clé du champ n'est pas nulle, cette situation signifie qu'il n'y a pas d'arborescence d'index secondaire pouvant être utilisée lors de la recherche, mais l'index secondaire contient les champs qui doivent être interrogés, donc l'index clusterisé (index clusterisé ) n'est plus recherché. L'index du cluster est relativement grand), et l'arborescence d'index secondaire est analysée à la place (l'arborescence d'index secondaire est relativement petite), et à ce stade, le type d'accès général est index, et l'arborescence d'index entière est analysée. . |
key | L'index utilisé par l'analyse réelle. S'il est nul, l'index n'est pas utilisé ; si un index de couverture est utilisé dans la requête, l'index n'apparaît que dans la liste des clés |
key_len | Le nombre d'octets utilisés dans l'index ; La longueur de l'index utilisé dans la requête peut être calculée via cette colonne. Sans perdre en précision, plus la longueur est courte, meilleure est la valeur affichée par key_len est la longueur maximale possible du champ d'index, et non la longueur réelle utilisée ; c'est-à-dire que key_len est basé sur la table. La définition est calculée, non récupérée de la table |
ref | montre quelle colonne de l'index est utilisée ; Si possible, il s'agit d'une constante, quelles colonnes ou constantes sont utilisées pour trouver la valeur sur la colonne d'index |
lignes | En fonction des statistiques de la table et de la sélection de l'index, estimez approximativement la lecture requise pour trouver l'enregistrement requis. nombre de lignes ; |
filtré | Le pourcentage de données restantes après filtrage par conditions de recherche. |
Extra | Contient des informations supplémentaires qui ne peuvent pas être affichées dans d'autres colonnes mais qui sont très importantes |
Séquence d'exécution de l'instruction select numéro de requête, contient un ensemble de nombres, indiquant l'ordre dans lequel les clauses de sélection ou les tables d'opérations sont exécutées dans la requête. Il existe trois situations :
Nom du type | Description |
---|---|
Id est le même. | Ordre d'exécution de haut en bas Suivant |
l'id est différent | S'il s'agit d'une sous-requête, le numéro de série de l'id sera incrémenté. Plus la valeur de l'id est grande, plus la priorité est élevée, plus elle sera exécutée tôt. |
l'identifiant est le même mais différent, et existe en même temps | Si l'identifiant est le même, il peut Il est considéré comme un groupe et exécuté séquentiellement de haut en bas. Parmi tous les groupes, plus il est grand. valeur d'identification, plus la priorité est élevée et plus elle est exécutée tôt |
est le type d'opération de lecture de données. Il a les types suivants :
Nom du type | Description |
---|---|
simple | Requête de sélection simple, la requête ne le fait pas. contient des sous-requêtes ou des unions ; |
primaire | Si la requête contient des sous-requêtes complexes, la requête la plus externe sera marquée |
sous-requête | contient des sous-requêtes dans la liste de sélection ou où ; |
sous-requête dépendante | Le premier SELECT de la sous-requête dépend de la requête externe. Autrement dit, la sous-requête dépend des résultats de la requête externe. |
derived | Les sous-requêtes contenues dans la liste from sont marquées comme DERIVED (table dérivée). MySQL exécutera récursivement ces sous-requêtes et placera les résultats dans la table temporaire |
union | Si la seconde sélection est effectuée ; apparaît après l'union, elle est marquée comme union. Si union est incluse dans la sous-requête de la clause from, la sélection externe sera marquée comme |
union result | de la table union (c'est-à-dire le résultat de fusion d'union Obtenez les résultats de la requête de sélection dans l'ensemble); |
meterialized | Table matérialisée, lorsque la sous-requête est associée à la requête, les résultats de la sous-requête sont stockés dans la table temporaire matérialisée, puis la table principale est correspondant en fonction des données de la table temporaire. |
union dépendante | UNION La deuxième instruction de requête ou les suivantes dans UNION dépend de la requête externe |
, si la requête utilise un alias, donc ce qui est affiché ici est l'alias, s'il n'y a aucune opération sur la table de données, alors celui-ci est affiché comme nul, ou il peut s'agir de l'un des éléments suivants :
Type de nom | Description |
---|---|
N > |
表示这个是临时表,后边的N就是执行计划中的 id,表示结果来自于这个查询产生。 |
N > |
与 |
La requête correspondra aux partitions des enregistrements. La valeur NULL
est utilisée pour les tables non partitionnées. NULL
用于非分区表。
依次从好到差:
system
>const
>eq_ref
>ref
>ref_or_null
>range
>index
>ALL
除了all
之外,其他的type
都可以使用到索引,除了index_merge
之外,其他的type
只可以用到一个索引。
我们自己创建一系列表来实验下:
SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for goods -- ---------------------------- DROP TABLE IF EXISTS `goods`; CREATE TABLE `goods` ( `id` int(11) NOT NULL, `sn` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of goods -- ---------------------------- INSERT INTO `goods` VALUES (1, 'sn123456', '衣服'); -- ---------------------------- -- Table structure for sku -- ---------------------------- DROP TABLE IF EXISTS `sku`; CREATE TABLE `sku` ( `id` int(11) NOT NULL, `goods_id` int(11) NOT NULL, `status` int(11) NOT NULL, `deleted` int(11) NOT NULL, `barcode` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE, UNIQUE INDEX `index_2`(`name`) USING BTREE, INDEX `index_1`(`goods_id`, `status`, `deleted`, `barcode`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of sku -- ---------------------------- INSERT INTO `sku` VALUES (1, 1, 1, 0, 'kt123456', '黑色'); SET FOREIGN_KEY_CHECKS = 1;
表只有一行记录(等于系统表),这是 const 类型的特例,平时不会出现,这个也可忽略不计;
表示通过索引一次就找到了,const 用于比较 primary key 或者 unique 索引。因为只匹配一行记录,所以很快。 如果将主键置于 where 列表中,mysql 就能将该查询转换成一个常量;
EXPLAIN SELECT * FROM sku WHERE id=1;复制代码
唯一性索引扫描,对于每一个索引键,表中只有一条记录与之匹配,常用于主键或唯一索引扫描;此类型通常出现在多表的 join 等值查询,表示对于前表的每一个结果,都只能匹配到后表的一行结果,查询效率较高。
EXPLAIN SELECT * FROM sku,goods WHERE sku.goods_id=goods.id;
非唯一性索引扫描,返回匹配某个单独值得所有行,本质上也是一种索引访问,它返回所有匹配某个单独值的行,然而,它可能会找到多个符合条件的行,所以它应该属于查找和扫描的混合体;
EXPLAIN SELECT * FROM sku WHERE goods_id=1;
二级索引等值比较同时限定 is null 。
EXPLAIN SELECT * FROM sku WHERE name='123456' or name IS NULL;
只检索给定范围的行,使用一个索引来选择行,key列显示使用哪个索引,一般就是在你的 where 语句中出现了 between、<、>、in 等的查询;这种范围索引扫描比全表扫描要好,因为它只需要开始于索引的某一个点,结束于另一个点,不用扫描全部索引;
EXPLAIN SELECT * FROM sku WHERE id BETWEEN 1 and 10;
index 和 all 区别为 index 类型只遍历索引树,这通常比 all 快,因为索引文件通常比数据文件小;也就是说虽然 all 和 index 都是读写表,但 index 是从索引中读取的,而 all 是从硬盘中读的;
EXPLAIN SELECT barcode FROM sku WHERE deleted=0;
也就是全表扫描;
EXPLAIN SELECT * FROM sku WHERE deleted=0;
查询可能使用到的索引都会在这里列出来。
查询真正使用到的索引,select_type
为index_merge
时,这里可能出现两个以上的索引,其他的select_type
这里只会出现一个。
key_len 表示该列计算查询中使用的索引的长度。例如:SELECT * FROM table where age = 1 and name like 'xx'
,假设 age 是 int 类型且不可为 null;name 是 varchar(20) 类型且可以为 null,编码为 utf8。若以这两个字段为索引查询,那么 key_len 的值为 4 + 3 * 20 + 2 + 1 = 67
🎜🎜En plus desystem
>const
> ;eq_ref
>ref
>ref_or_null
>range
>index
> ;TOUS
🎜
all
, d'autres type
peuvent utiliser l'index, sauf index_merge, les autres <code>type
ne peuvent utiliser qu'un seul index. 🎜🎜Créons nous-mêmes une série de tableaux pour expérimenter : 🎜🎜🎜🎜🎜rrreeeselect_type
est index_merge
, cela peut apparaître pour. plus de deux index, un seul des autres select_type
apparaîtra ici. 🎜SELECT * FROM table où age = 1 et nom comme 'xx'
, en supposant que l'âge est de type int et ne peut pas être nul ; le nom est de type varchar(20) et peut être nul, et l'encodage est utf8. Si ces deux champs sont utilisés comme requêtes d'index, alors la valeur de key_len est 4 + 3 * 20 + 2 + 1 = 67
. Les règles de calcul spécifiques sont telles qu'indiquées dans le tableau ci-dessous : 🎜Type de valeur | Nom de la valeur | Description |
---|---|---|
Chaîne | CHAR(n) | n Longueur en octets |
VARCHAR (n) | S'il s'agit d'un encodage utf8, c'est 3 n + 2 octets ; s'il s'agit d'un encodage utf8mb4, c'est 4 n + 2 octets. | |
TYPE NUMÉRIQUE | TINYINT | 1 octet |
SMALLINT | 2 octets | |
MEDIUMINT | 3 octets | |
INT | 4 octets | |
BIGINT | 8 octets | |
type d'heure | DATE | 3 octets |
TIMESTAMP | 4 octets | |
DATETIME | 8 octets | |
Attribut de champ | L'attribut NULL occupe un octet. Si un champ est NOT NULL, il n'est pas occupé. |
S'il s'agit d'une requête équivalente constante, const
sera affiché ici. S'il s'agit d'une requête de connexion, le plan d'exécution de la table pilotée affichera les champs associés de. la table de pilotage. Si la condition utilise une expression ou une fonction, ou si la colonne de condition subit une conversion implicite interne, elle peut être affichée sous la forme func
. const
,如果是连接查询,被驱动表的执行计划这里会显示驱动表的关联字段,如果是条件使用了表达式或者函数,或者条件列发生了内部隐式转换,这里可能显示为func
。
这里是执行计划中估算的扫描行数,不是精确值。
使用explain extended
时会出现这个列,5.7
之后的版本默认就有这个字段,不需要使用explain extended
了。这个字段表示存储引擎返回的数据在server层过滤后,剩下多少满足查询的记录数量的比例,注意是百分比,不是具体记录数。
这个列可以显示的信息非常多,有几十种,常用的有:
1、distinct:在select
部分使用了distinct
关键字
2、no tables used:不带from
字句的查询或者From dual
查询。使用not in()
形式子查询或not exists()
运算符的连接查询,这种叫做反连接。即,一般连接查询是先查询内表,再查询外表,反连接就是先查询外表,再查询内表。
3、using filesort:说明mysql
会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取。mysql
中无法利用索引完成的排序操作称为“文件排序”。排序时无法使用到索引时,就会出现这个。常见于order by
语句中,需要尽快优化
4、using index:查询时不需要回表查询,直接通过索引就可以获取查询的数据。
5、using join buffer(block nested loop),using join buffer(batched key accss) :5.6.x
之后的版本优化关联查询的BNL
,BKA
特性。主要是减少内表的循环数量以及比较顺序地扫描查询。
6、using sort_union,using_union,using intersect,using sort_intersection:
and
的各个索引的条件时,该信息表示是从处理结果获取交集or
连接各个使用索引的条件时,该信息表示从处理结果获取并集and
和or
查询信息量大时,先查询主键,然后进行排序合并后,才能读取记录并返回。7、using temporary:表示使用了临时表存储中间结果。临时表可以是内存临时表和磁盘临时表,执行计划中看不出来,需要查看status
变量,used_tmp_table
,used_tmp_disk_table
才能看出来。常见于order by
和分组查询group by
。group by
一定要遵循所建索引的顺序与个数。需要尽快优化
8、using where:表示存储引擎返回的记录并不是所有的都满足查询条件,需要在server
层进行过滤。查询条件中分为限制条件和检查条件,5.6
之前,存储引擎只能根据限制条件扫描数据并返回,然后server
层根据检查条件进行过滤再返回真正符合查询的数据。5.6.x
之后支持ICP
特性(index condition pushdown,索引下推),可以把检查条件也下推到存储引擎层,不符合检查条件和限制条件的数据,直接不读取,这样就大大减少了存储引擎扫描的记录数量。extra
列显示using index condition
9、firstmatch(tb_name) :5.6.x
开始引入的优化子查询的新特性之一,常见于where
字句含有in()
类型的子查询。如果内表的数据量比较大,就可能出现这个
10、loosescan(m..n) :5.6.x
之后引入的优化子查询的新特性之一,在in()
(11) filtré
🎜🎜Cette colonne apparaîtra lors de l'utilisation deexplain extend
, et la version après 5.7
sera être par défaut Avec ce champ, il n'est pas nécessaire d'utiliser explain extend
. Ce champ indique la proportion des enregistrements restants qui satisfont la requête après le filtrage des données renvoyées par le moteur de stockage au niveau de la couche serveur. Notez qu'il s'agit d'un pourcentage et non d'un nombre spécifique d'enregistrements. 🎜distinct
est utilisé dans la partie >selectfrom
ou . À partir d'une requête double code>. Utilisez la sous-requête de formulaire <code>pas dans()
ou la requête de jointure de l'opérateur pas existe()
, appelée anti-jointure. Autrement dit, une requête de jointure générale interroge d'abord la table interne, puis la table externe, tandis qu'une requête anti-jointure interroge d'abord la table externe, puis la table interne. 🎜🎜🎜3. using filesort🎜 : Description mysql
triera les données à l'aide d'un index externe au lieu de les lire dans l'ordre de l'index dans le tableau. L'opération de tri qui ne peut pas être effectuée à l'aide des index dans mysql
est appelée "tri de fichiers". Cela se produit lorsque l'index ne peut pas être utilisé lors du tri. Couramment vu dans les instructions order by
, il doit être optimisé dès que possible🎜🎜🎜4 à l'aide d'index🎜 : il n'est pas nécessaire de revenir à la table lors de l'interrogation, et les données de la requête peuvent être. obtenu directement via l’index. 🎜🎜🎜5. en utilisant le tampon de jointure (boucle imbriquée de bloc), en utilisant le tampon de jointure (accès par clé par lots)🎜 : 5.6.x
et les versions ultérieures optimisent BNL
pour les requêtes associées, Fonctionnalité BKA
. L'objectif principal est de réduire le nombre de boucles dans la table interne et d'analyser la requête de manière séquentielle. 🎜🎜🎜6. using sort_union, using_union, using intersect, using sort_intersection : 🎜🎜et
, cette information indique qu'il s'agit d'un traité à partir de l'intersection d'acquisition des résultatsou
est utilisé pour connecter des conditions à l'aide d'index, cette information indique que l'union est obtenue à partir des résultats du traitementet
et de ou
pour interroger une grande quantité d'informations. La clé primaire est. d'abord interrogé, puis trié et fusionné. Ce n'est qu'alors que l'enregistrement peut être lu et renvoyé. status
, used_tmp_table
et used_tmp_disk_table.
pour le voir sortir. Couramment utilisé dans order by
et dans les requêtes de groupe group by
. group by
doit suivre l'ordre et le nombre d'index créés. Besoin d'optimiser dès que possible🎜🎜🎜8. en utilisantwhere🎜 : indique que tous les enregistrements renvoyés par le moteur de stockage ne répondent pas aux conditions de requête et doivent être filtrés au niveau de la couche serveur
. Les conditions de requête sont divisées en conditions de restriction et conditions d'inspection. Avant 5.6
, le moteur de stockage pouvait uniquement analyser les données et les renvoyer en fonction des conditions de restriction, puis la couche serveur
le faisait. filtrez-le et renvoyez-le en fonction des conditions d’inspection. Données qui correspondent réellement à la requête. 5.6.x
prend en charge la fonctionnalité ICP
(indexation des conditions, index pushdown) après 5.6.x
. Les conditions de vérification peuvent également être poussées vers le bas. la couche moteur de stockage, qui ne répond pas aux conditions et restrictions de contrôle. Les données ne sont pas lues directement, ce qui réduit considérablement le nombre d'enregistrements analysés par le moteur de stockage. La colonne extra
affiche 🎜using index condition🎜🎜🎜🎜9, firstmatch(tb_name)🎜 : l'une des nouvelles fonctionnalités de sous-requête optimisée introduite dans 5.6.x
, couramment vue in La clause where
contient une sous-requête de type in()
. Si la quantité de données dans la table interne est relativement importante, cela peut se produire🎜🎜🎜10 Loosescan(m..n)🎜 : L'une des nouvelles fonctionnalités des sous-requêtes optimisées introduites après 5.6.x
. , dans Dans une sous-requête de type in()
, lorsque la sous-requête peut renvoyer des enregistrements en double, cela peut se produire🎜🎜🎜4 Expliquez l'objectif principal🎜🎜🎜En général, nous n'avons besoin que de prêter attention à un. quelques colonnes dans les résultats : 🎜Nom de la colonne | Remarques |
---|---|
type | Ce type de connexion à la table de requête, à partir d'ici vous pouvez voir l'efficacité approximative de cette requête |
clé | L'index final sélectionné, sinon In en termes d'indexation, l'efficacité de cette requête est généralement très mauvaise |
key_len | La longueur réelle de l'index utilisé pour le filtrage des résultats dans cette requête |
rows | Le nombre estimé d'enregistrements qui doivent être analysés , le nombre estimé d'enregistrements à analyser est plus petit, c'est mieux |
Extra | Informations supplémentaires supplémentaires, confirment principalement si les deux situations de Utilisation du tri de fichiers et Utilisation temporaire se produitUsing filesort 、Using temporary 这两种情况 |
再来看下Extra
列中需要注意出现的几种情况:
关键字 | 备注 |
---|---|
Using filesort | 将用外部排序而不是按照索引顺序排列结果,数据较少时从内存排序,否则需要在磁盘完成排序,代价非常高,需要添加合适的索引 |
Using temporary | 需要创建一个临时表来存储结果,这通常发生在对没有索引的列进行GROUP BY 时,或者ORDER BY 里的列不都在索引里,需要添加合适的索引
|
Using index | 表示MySQL 使用覆盖索引避免全表扫描,不需要再到表中进行二次查找数据,这是比较好的结果之一。注意不要和type 中的index 类型混淆 |
Using where | 通常是进行了全表/全索引扫描后再用WHERE 子句完成结果过滤,需要添加合适的索引
|
Impossible WHERE | 对Where 子句判断的结果总是false而不能选择任何数据,例如where 1=0 ,无需过多关注 |
Select tables optimized away | 使用某些聚合函数来访问存在索引的某个字段时,优化器会通过索引直接一次定位到所需要的数据行完成整个查询,例如MIN()MAX()
|
Extra
: 🎜Mots clés🎜🎜Remarques🎜🎜🎜🎜 🎜🎜L'utilisation de filesort🎜🎜 utilisera un tri externe au lieu de trier les résultats dans l'ordre de l'index. Trier à partir de la mémoire lorsqu'il y a moins de données, sinon le tri doit être effectué sur le disque, ce qui coûte très cher. 🎜Besoin d'ajouter un index approprié. 🎜🎜🎜🎜🎜Utilisation de temporaire🎜🎜Vous devez créer une table temporaire pour stocker les résultats, ce qui se produit généralement lorsqu'il n'y a pas d'index Lorsque les colonnes de GROUP BY
sont exécutées, ou les colonnes de ORDER BY ne sont pas tous dans l'index, vous devez ajouter les index appropriés🎜🎜🎜🎜🎜L'utilisation d'index🎜🎜 signifie queMySQL utilise des index de couverture pour éviter les analyses de table complètes, et là Il n'est pas nécessaire de rechercher les données deux fois dans le tableau. C'est l'un des meilleurs résultats. Attention à ne pas le confondre avec le type index
dans type
🎜🎜🎜🎜Utiliser Where🎜🎜Effectuez généralement une analyse complète de la table/de l'index avant d'utiliser WHERE complète le filtrage des résultats. 🎜Vous devez ajouter un index approprié🎜🎜🎜🎜🎜Impossible WHERE🎜🎜Le résultat du jugement de la clause <code>Where
est toujours faux et aucune donnée ne peut être sélectionnée. , comme où 1=0
, pas besoin d'y prêter trop d'attention🎜🎜🎜🎜Sélectionner des tables optimisées🎜🎜Lors de l'utilisation de certaines fonctions d'agrégation pour accéder à un champ avec un index, l'optimiseur localisera directement le champ requis à la fois via l'index La ligne de données complète la requête entière, telle que MIN()MAX()
, qui est également l'un des meilleurs résultats🎜🎜🎜🎜🎜[Recommandation associée : 🎜 tutoriel vidéo MySQL🎜]🎜
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!