Maison  >  Article  >  base de données  >  Analyse détaillée des trois nouvelles méthodes d'index ajoutées dans MySQL 8.x (partage de résumé)

Analyse détaillée des trois nouvelles méthodes d'index ajoutées dans MySQL 8.x (partage de résumé)

WBOY
WBOYavant
2021-12-30 18:39:222479parcourir

Cet article vous apporte des connaissances sur les trois nouveaux index ajoutés dans la version MySQL 8.x. Il existe trois nouvelles méthodes d'indexation dans MySQL 8.x : l'index caché, l'index descendant et l'index de fonction. J'espère que cela sera utile à tout le monde.

Analyse détaillée des trois nouvelles méthodes d'index ajoutées dans MySQL 8.x (partage de résumé)

1. Index caché

1. Aperçu de l'index caché

  • MySQL 8.0 commence à prendre en charge l'index invisible, l'index invisible.
  • Les index cachés ne sont pas utilisés par l'optimiseur, mais doivent quand même être conservés.
  • Scénarios d'application : suppression logicielle, publication en niveaux de gris.

Dans les versions précédentes de MySQL, les index ne pouvaient être supprimés explicitement que si le mauvais index était trouvé après la suppression, l'index supprimé ne pouvait être rajouté qu'en créant un index si la quantité de données dans la base de données était très grande. grande, ou la table est relativement grande, le coût de cette opération est très élevé.

Dans MySQL 8.0, il vous suffit d'abord de définir cet index comme index masqué afin que l'optimiseur de requêtes n'utilise plus cet index. Cependant, cet index doit toujours être maintenu par le backend MySQL à ce moment-là. cet index comme index masqué. Lorsque le système n'est pas affecté, supprimez complètement l'index. Il s’agit de la fonctionnalité de suppression logicielle.

La publication en niveaux de gris signifie que lors de la création d'un index, définissez d'abord l'index sur un index masqué, rendez l'index masqué visible à l'optimiseur de requête en modifiant le commutateur de l'optimiseur de requête et testez l'index via expliquer pour confirmer que l'index est valide. Si certaines requêtes peuvent utiliser cet index, vous pouvez le définir comme index visible pour obtenir l'effet de publication en niveaux de gris.

2. Opération d'index caché

(1) Connectez-vous à MySQL, créez la base de données testdb et créez une table de test t1 dans la base de données

mysql> create database if not exists testdb;
Query OK, 1 row affected (0.58 sec)
mysql> use testdb;
Database changed
mysql> create table if not exists t1(i int, j int);
Query OK, 0 rows affected (0.05 sec)

(2) Créez un index sur le champ i, comme indiqué ci-dessous.

mysql> create index i_idx on t1(i);
Query OK, 0 rows affected (0.34 sec)
Records: 0  Duplicates: 0  Warnings: 0

(3) Créez un index caché sur le champ j. Lors de la création d'un index caché, il vous suffit d'ajouter le mot-clé invisible après l'instruction qui crée l'index, comme indiqué ci-dessous

mysql> create index j_idx on t1(j) invisible;
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

(4) Vérifiez la situation de l'index dans la table t1, comme indiqué ci-dessous

mysql> show index from t1 \G
*************************** 1. row ***************************
        Table: t1
   Non_unique: 1
     Key_name: i_idx
 Seq_in_index: 1
  Column_name: i
    Collation: A
  Cardinality: 0
     Sub_part: NULL
       Packed: NULL
         Null: YES
   Index_type: BTREE
      Comment: 
Index_comment: 
      Visible: YES
   Expression: NULL
*************************** 2. row ***************************
        Table: t1
   Non_unique: 1
     Key_name: j_idx
 Seq_in_index: 1
  Column_name: j
    Collation: A
  Cardinality: 0
     Sub_part: NULL
       Packed: NULL
         Null: YES
   Index_type: BTREE
      Comment: 
Index_comment: 
      Visible: NO
   Expression: NULL
2 rows in set (0.02 sec)

vous pouvez voir qu'il y a deux index dans la table t1, l'un est i_idx et l'autre est j_idx. L'attribut Visible de i_idx est OUI, indiquant que cet index est visible ; de j_idx est NON, indiquant que cet index n'est pas visible.

(5) Vérifiez l'utilisation de ces deux index par l'optimiseur de requêtes.
Tout d’abord, utilisez le champ i pour interroger, comme indiqué ci-dessous.

mysql> explain select * from t1 where i = 1 \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t1
   partitions: NULL
         type: ref
possible_keys: i_idx
          key: i_idx
      key_len: 5
          ref: const
         rows: 1
     filtered: 100.00
        Extra: NULL
1 row in set, 1 warning (0.02 sec)
可以看到,查询优化器会使用i字段的索引进行优化。
接下来,使用字段j进行查询,如下所示。
mysql> explain select * from t1 where j = 1 \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t1
   partitions: NULL
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 1
     filtered: 100.00
        Extra: Using where
1 row in set, 1 warning (0.00 sec)

Vous pouvez voir que l'optimiseur de requêtes n'utilise pas l'index caché sur le champ j, mais utilise une analyse complète de la table pour interroger les données.

(6) Rendre les index cachés visibles pour l'optimiseur
Une nouvelle méthode de test est fournie dans MySQL 8.x Vous pouvez activer un certain paramètre via un commutateur de l'optimiseur pour rendre l'index caché visible pour l'optimiseur de requêtes.
Vérifiez les commutateurs de l'optimiseur de requête comme indiqué ci-dessous.

mysql> select @@optimizer_switch \G 
*************************** 1. row ***************************
@@optimizer_switch: index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,engine_condition_pushdown=on,index_condition_pushdown=on,mrr=on,mrr_cost_based=on,block_nested_loop=on,batched_key_access=off,materialization=on,semijoin=on,loosescan=on,firstmatch=on,duplicateweedout=on,subquery_materialization_cost_based=on,use_index_extensions=on,condition_fanout_filter=on,derived_merge=on,use_invisible_indexes=off,skip_scan=on,hash_join=on
1 row in set (0.00 sec)

Ici, vous pouvez voir la valeur d'attribut suivante :

use_invisible_indexes=off

indique si l'optimiseur utilise des index invisibles. La valeur par défaut est désactivée et non utilisée.
Ensuite, activez l'optimiseur de requêtes pour utiliser des index invisibles au niveau de la session dans MySQL, comme indiqué ci-dessous.

mysql> set session optimizer_switch="use_invisible_indexes=on";
Query OK, 0 rows affected (0.00 sec)

Ensuite, vérifiez à nouveau les paramètres de commutation de l'optimiseur de requête, comme indiqué ci-dessous

mysql> select @@optimizer_switch \G
*************************** 1. row ***************************
@@optimizer_switch: index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,engine_condition_pushdown=on,index_condition_pushdown=on,mrr=on,mrr_cost_based=on,block_nested_loop=on,batched_key_access=off,materialization=on,semijoin=on,loosescan=on,firstmatch=on,duplicateweedout=on,subquery_materialization_cost_based=on,use_index_extensions=on,condition_fanout_filter=on,derived_merge=on,use_invisible_indexes=on,skip_scan=on,hash_join=on
1 row in set (0.00 sec)

À ce moment, vous pouvez voir use_invisible_indexes=on, indiquant que l'index caché est visible pour l'optimiseur de requête.

Analysez à nouveau les données de la requête à l'aide du champ j de la table t1, comme indiqué ci-dessous.

mysql> explain select * from t1 where j = 1 \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t1
   partitions: NULL
         type: ref
possible_keys: j_idx
          key: j_idx
      key_len: 5
          ref: const
         rows: 1
     filtered: 100.00
        Extra: NULL
1 row in set, 1 warning (0.00 sec)

Vous pouvez voir que l'optimiseur de requête utilise l'index caché sur le champ j pour optimiser la requête.

(7) Définissez l'index visible et invisible
Définissez l'index caché du champ j sur visible, comme indiqué ci-dessous.

mysql> alter table t1 alter index j_idx visible;
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

Définissez l'index du champ j sur invisible comme indiqué ci-dessous.

mysql> alter table t1 alter index j_idx invisible;
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

(8) La clé primaire dans MySQL ne peut pas être définie comme index invisible
Il convient de noter que dans MySQL, la clé primaire ne peut pas être définie comme invisible.
Créez une table de test t2 dans la base de données testdb, comme indiqué ci-dessous.

mysql> create table t2(i int not null);
Query OK, 0 rows affected (0.01 sec)

Ensuite, créez une clé primaire invisible dans la table t2, comme indiqué ci-dessous

mysql> alter table t2 add primary key pk_t2(i) invisible; 
ERROR 3522 (HY000): A primary key index cannot be invisible

Vous pouvez voir que l'instruction SQL signale une erreur à ce moment-là et que la clé primaire ne peut pas être définie comme index invisible.

2. Index décroissant

1. Présentation de l'index décroissant

  • MySQL 8.0 prend réellement en charge l'index décroissant.
  • Seul le moteur de stockage InnoDB prend en charge les index descendants, et seuls les index descendants BTREE sont pris en charge.
  • MySQL 8.0 n'effectue plus de tri implicite pour les opérations GROUP BY

2. Opération d'index descendant

(1) Syntaxe prise en charge dans MySQL 5.7
Tout d'abord, créez la base de données de test testdb dans MySQL 5.7, créez dans la base de données testdb Table de test t2 est comme indiqué ci-dessous.

mysql> create database if not exists testdb;
Query OK, 0 rows affected (0.71 sec)
mysql> use testdb;
Database changed
mysql> create table if not exists t2(c1 int, c2 int, index idx1(c1 asc, c2 desc));
Query OK, 0 rows affected (0.71 sec)

Parmi eux, un index nommé idx1 est créé dans la table t2. Le champ c1 de l'index est trié par ordre croissant et le champ c2 est trié par ordre décroissant.

Ensuite, vérifiez les informations de création de la table t2, comme indiqué ci-dessous

mysql> show create table t2 \G*************************** 1. row ***************************
       Table: t2Create Table: CREATE TABLE `t2` (
  `c1` int(11) DEFAULT NULL,
  `c2` int(11) DEFAULT NULL,
  KEY `idx1` (`c1`,`c2`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb41 row in set (0.16 sec)

Vous pouvez voir que MySQL version 5.7 n'a pas d'informations de tri pour les champs c1 et c2 dans les informations de création de table, et la valeur par défaut est l'ordre croissant.

(2) Syntaxe prise en charge dans MySQL 8.0
Créez la table t2 dans MySQL 8. Dans MySQL 8.x, les informations de tri des champs existent dans l'index créé.

(3) L'utilisation des index par l'optimiseur de requêtes dans MySQL 5.7

Tout d'abord, insérez quelques données dans la table t2, comme indiqué ci-dessous.

mysql> insert into t2(c1, c2) values(1, 100), (2, 200), (3, 150), (4, 50);
Query OK, 4 rows affected (0.19 sec)
Records: 4  Duplicates: 0  Warnings: 0

接下来,查询t2表中的数据,如下所示。

mysql> select * from t2;
+------+------+
| c1   | c2   |
+------+------+
|    1 |  100 |
|    2 |  200 |
|    3 |  150 |
|    4 |   50 |
+------+------+
4 rows in set (0.00 sec)

可以看到,t2表中的数据插入成功。

接下来,查看查询优化器对索引的使用情况,这里,查询语句按照c1字段升序,按照c2字段降序,如下所示。

mysql> explain select * from t2 order by c1, c2 desc \G*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t2
   partitions: NULL
         type: indexpossible_keys: NULL
          key: idx1
      key_len: 10
          ref: NULL
         rows: 4
     filtered: 100.00
        Extra: Using index; Using filesort1 row in set, 1 warning (0.12 sec)

可以看到,在MySQL 5.7中,按照c2字段进行降序排序,并没有使用索引。

(4)MySQL 8.x中查询优化器对降序索引的使用情况。
查看查询优化器对降序索引的使用情况。
首先,在表t2中插入一些数据,如下所示。

mysql> insert into t2(c1, c2) values(1, 100), (2, 200), (3, 150), (4, 50);
Query OK, 4 rows affected (0.00 sec)
Records: 4  Duplicates: 0  Warnings: 0

接下来,查询t2表中的数据,如下所示。

mysql> select * from t2;
+------+------+
| c1   | c2   |
+------+------+
|    1 |  100 |
|    2 |  200 |
|    3 |  150 |
|    4 |   50 |
+------+------+
4 rows in set (0.00 sec)

可以看到,t2表中的数据插入成功。

在MySQL中如果创建的是升序索引,则指定查询的时候,只能按照升序索引的方式指定查询,这样才能使用升序索引。

接下来,查看查询优化器对索引的使用情况,这里,查询语句按照c1字段升序,按照c2字段降序,如下所示。

mysql> explain select * from t2 order by c1, c2 desc \G*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t2
   partitions: NULL
         type: indexpossible_keys: NULL
          key: idx1
      key_len: 10
          ref: NULL
         rows: 4
     filtered: 100.00
        Extra: Using index1 row in set, 1 warning (0.00 sec)

可以看到,在MySQL 8.x中,按照c2字段进行降序排序,使用了索引。

使用c1字段降序,c2字段升序排序,如下所示。

mysql> explain select * from t2 order by c1 desc, c2 \G*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t2
   partitions: NULL
         type: indexpossible_keys: NULL
          key: idx1
      key_len: 10
          ref: NULL
         rows: 4
     filtered: 100.00
        Extra: Backward index scan; Using index1 row in set, 1 warning (0.00 sec)

可以看到,在MySQL 8.x中仍然可以使用索引,并使用了索引的反向扫描。

(5)MySQL 8.x中不再对GROUP BY进行隐式排序

在MySQL 5.7中执行如下命令,按照c2字段进行分组,查询每组中数据的记录条数。

mysql> select count(*), c2 from t2 group by c2;
+----------+------+
| count(*) | c2   |
+----------+------+
|        1 |   50 |
|        1 |  100 |
|        1 |  150 |
|        1 |  200 |
+----------+------+
4 rows in set (0.18 sec)

可以看到,在MySQL 5.7中,在c2字段上进行了排序操作。

在MySQL 8.x中执行如下命令,按照c2字段进行分组,查询每组中数据的记录条数。

mysql> select count(*), c2 from t2 group by c2;
+----------+------+
| count(*) | c2   |
+----------+------+
|        1 |  100 |
|        1 |  200 |
|        1 |  150 |
|        1 |   50 |
+----------+------+
4 rows in set (0.00 sec)

可以看到,在MySQL 8.x中,在c2字段上并没有进行排序操作。

在MySQL 8.x中如果需要对c2字段进行排序,则需要使用order by语句明确指定排序规则,如下所示。

mysql> select count(*), c2 from t2 group by c2 order by c2;
+----------+------+
| count(*) | c2   |
+----------+------+
|        1 |   50 |
|        1 |  100 |
|        1 |  150 |
|        1 |  200 |
+----------+------+
4 rows in set (0.00 sec)

三、函数索引

1.函数索引概述

  • MySQL 8.0.13开始支持在索引中使用函数(表达式)的值。
  • 支持降序索引,支持JSON数据的索引
  • 函数索引基于虚拟列功能实现

2.函数索引操作

(1)创建测试表t3
在testdb数据库中创建一张测试表t3,如下所示。

mysql> create table if not exists t3(c1 varchar(10), c2 varchar(10));
Query OK, 0 rows affected (0.01 sec)

(2)创建普通索引
在c1字段上创建普通索引

mysql> create index idx1 on t3(c1);
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

(3)创建函数索引
在c2字段上创建一个将字段值转化为大写的函数索引,如下所示。

mysql> create index func_index on t3 ((UPPER(c2)));
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0

(4)查看t3表上的索引信息,如下所示。

mysql> show index from t3 \G*************************** 1. row ***************************
        Table: t3
   Non_unique: 1
     Key_name: idx1
 Seq_in_index: 1
  Column_name: c1
    Collation: A
  Cardinality: 0
     Sub_part: NULL
       Packed: NULL
         Null: YES
   Index_type: BTREE
      Comment: 
Index_comment: 
      Visible: YES
   Expression: NULL*************************** 2. row ***************************
        Table: t3
   Non_unique: 1
     Key_name: func_index
 Seq_in_index: 1
  Column_name: NULL
    Collation: A
  Cardinality: 0
     Sub_part: NULL
       Packed: NULL
         Null: YES
   Index_type: BTREE
      Comment: 
Index_comment: 
      Visible: YES
   Expression: upper(`c2`)2 rows in set (0.01 sec)

(5)查看查询优化器对两个索引的使用情况
首先,查看c1字段的大写值是否等于某个特定的值,如下所示。

mysql> explain select * from t3 where upper(c1) = 'ABC' \G*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t3
   partitions: NULL
         type: ALLpossible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 1
     filtered: 100.00
        Extra: Using where1 row in set, 1 warning (0.00 sec)

可以看到,没有使用索引,进行了全表扫描操作。

接下来,查看c2字段的大写值是否等于某个特定的值,如下所示。

mysql> explain select * from t3 where upper(c2) = 'ABC' \G 
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t3
   partitions: NULL
         type: ref
possible_keys: func_index          key: func_index
      key_len: 43
          ref: const         rows: 1
     filtered: 100.00
        Extra: NULL1 row in set, 1 warning (0.00 sec)

可以看到,使用了函数索引。

(6)函数索引对JSON数据的索引
首先,创建测试表emp,并对JSON数据进行索引,如下所示。

mysql> create table if not exists emp(data json, index((CAST(data->>'$.name' as char(30)))));
Query OK, 0 rows affected (0.02 sec)

上述SQL语句的解释如下:

  • JSON数据长度不固定,如果直接对JSON数据进行索引,可能会超出索引长度,通常,会只截取JSON数据的一部分进行索引。
  • CAST()类型转换函数,把数据转化为char(30)类型。使用方式为CAST(数据 as 数据类型)。
  • data ->> '$.name’表示JSON的运算符

简单的理解为,就是取name节点的值,将其转化为char(30)类型。

接下来,查看emp表中的索引情况,如下所示。

mysql> show index from emp \G
*************************** 1. row ***************************
        Table: emp
   Non_unique: 1
     Key_name: functional_index
 Seq_in_index: 1
  Column_name: NULL
    Collation: A
  Cardinality: 0
     Sub_part: NULL
       Packed: NULL
         Null: YES
   Index_type: BTREE
      Comment: 
Index_comment: 
      Visible: YES
   Expression: cast(json_unquote(json_extract(`data`,_utf8mb4\'$.name\')) as char(30) charset utf8mb4)
1 row in set (0.00 sec)

(7)函数索引基于虚拟列实现
首先,查看t3表的信息,如下所示。

mysql> desc t3;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| c1    | varchar(10) | YES  | MUL | NULL    |       |
| c2    | varchar(10) | YES  |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.00 sec)

在c1上建立了普通索引,在c2上建立了函数索引。

接下来,在t3表中添加一列c3,模拟c2上的函数索引,如下所示。

mysql> alter table t3 add column c3 varchar(10) generated always as (upper(c1));
Query OK, 0 rows affected (0.03 sec)
Records: 0  Duplicates: 0  Warnings: 0

c3列是一个计算列,c3字段的值总是使用c1字段转化为大写的结果。

接下来,向t3表中插入一条数据,其中,c3列是一个计算列,c3字段的值总是使用c1字段转化为大写的结果,在插入数据的时候,不需要为c3列插入数据,如下所示。

mysql> insert into t3(c1, c2) values ('abc', 'def');
Query OK, 1 row affected (0.00 sec)

查询t3表中的数据,如下所示。

mysql> select * from t3;
+------+------+------+
| c1   | c2   | c3   |
+------+------+------+
| abc  | def  | ABC  |
+------+------+------+
1 row in set (0.00 sec)

可以看到,并不需要向c3列中插入数据,c3列的数据为c1字段的大写结果数据。

如果想模拟函数索引的效果,则可以使用如下方式。
首先,在c3列上添加索引,如下所示。

mysql> create index idx3 on t3(c3);
Query OK, 0 rows affected (0.11 sec)
Records: 0  Duplicates: 0  Warnings: 0

接下来,再次查看c1字段的大写值是否等于某个特定的值,如下所示。

mysql> explain select * from t3 where upper(c1) = 'ABC' \G*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t3
   partitions: NULL
         type: ref
possible_keys: idx3          key: idx3
      key_len: 43
          ref: const         rows: 1
     filtered: 100.00
        Extra: NULL1 row in set, 1 warning (0.00 sec)

此时,就使用了idx3索引。

推荐学习: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!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer