+----+-------------+-------+------+---------------+------+---------+------+--------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+--------+-------+
| 1 | SIMPLE | t | ALL | NULL | NULL | NULL | NULL | 100332 | NULL |
+----+-------------+-------+------+---------------+------+---------+------+--------+-------+
1 row in set (0.04 sec)
从效率角度来看,这些查询方式有一个从最佳到最差的排序。
`key` 指的是在查询过程中实际被使用的索引名称。
`rows` 反映了查询过程中可能涉及的行数,这一数据是由MySQL抽样统计得出的,因此并不绝对精确。
`Extra` 列提供了额外的信息,通常用于指示是否利用了索引、是否需要排序操作,或者是否使用了临时表等。
接下来,我们将通过实例来深入分析这些概念。
表的结构如下,数据量10条:
`id` int(11) NOT NULL,
`a` int(11) DEFAULT NULL,
`b` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
+----+-------------+-------+------+---------------+------+---------+------+--------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+--------+-------+
| 1 | SIMPLE | t | ALL | NULL | NULL | NULL | NULL | 100332 | NULL |
+----+-------------+-------+------+---------------+------+---------+------+--------+-------+
1 row in set (0.04 sec)
接下来,我们将分别为字段 `a` 和 `b` 添加普通索引,并观察添加索引后 SQL 语句的表现。
Query OK, 0 rows affected (0.19 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> alter table t add index b_index(b);
Query OK, 0 rows affected (0.20 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> show index from t;
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| t | 0 | PRIMARY | 1 | id | A | 100332 | NULL | NULL | | BTREE | | |
| t | 1 | a_index | 1 | a | A | 100332 | NULL | NULL | YES | BTREE | | |
| t | 1 | b_index | 1 | b | A | 100332 | NULL | NULL | YES | BTREE | | |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
3 rows in set (0.00 sec)
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
| 1 | SIMPLE | t | ALL | a_index | NULL | NULL | NULL | 100332 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
1 row in set (0.00 sec)
上面提到的 SQL 语句中 `type` 显示为全表扫描(`ALL`),即使我们为字段 `a` 添加了索引(`a_index`),并且 `possible_keys` 也显示了该索引是可用的。但 `key` 显示为 `null`,意味着 MySQL 在实际执行查询时并没有使用这个索引。
原因在于,当使用 `SELECT *` 时,除了从索引 `a_index` 中筛选数据外,还需要回到主键索引中去查找字段 `b` 的值,因为字段 `b` 并没有包含在索引 `a_index` 中。这种回到主键索引查找的过程被称为“回表”。
在这个例子中,有大约 9 万条记录满足条件,意味着需要进行 9 万次回表操作。
由于全表扫描大约涉及 10 万条数据,而使用索引并进行回表操作会涉及 9 万次额外的查找,从 MySQL 优化器的角度来看,全表扫描的代价可能更低,因为它避免了额外的回表操作。
但这并不意味着只要有回表操作,索引就不会被使用。
是否使用索引取决于 MySQL 优化器对查询代价的评估。我们可以通过调整 SQL 语句来影响优化器的决策。
+----+-------------+-------+-------+---------------+---------+---------+------+------+-----------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-----------------------+
| 1 | SIMPLE | t | range | a_index | a_index | 5 | NULL | 999 | Using index condition |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-----------------------+
1 row in set (0.00 sec)
这次,`type` 的值变为了 `range`,而 `key` 为 `a_index`,这表示 `a` 索引被成功命中了。满足这条 SQL 查询条件的数据仅有 1000 条。MySQL 评估后认为,即使需要回表操作,这 1000 条数据的回表代价也低于全表扫描。
进一步观察,`Extra` 字段中的值为 `Using index condition`。这表示虽然索引被用到了,但仍然需要回表操作来获取更多的数据。
再看下面的 SQL 语句。
+----+-------------+-------+-------+---------------+---------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+---------+---------+------+------+--------------------------+
| 1 | SIMPLE | t | range | a_index | a_index | 5 | NULL | 999 | Using where; Using index |
+----+-------------+-------+-------+---------------+---------+---------+------+------+--------------------------+
1 row in set (0.00 sec)
+----+-------------+-------+-------+---------------+---------+---------+------+------+---------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+---------+---------+------+------+---------------------------------------+
| 1 | SIMPLE | t | range | a_index | a_index | 5 | NULL | 999 | Using index condition; Using filesort |
+----+-------------+-------+-------+---------------+---------+---------+------+------+---------------------------------------+
1 row in set (0.00 sec)
+----+-------------+-------+-------+------------------+---------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+------------------+---------+---------+------+------+--------------------------+
| 1 | SIMPLE | t | range | a_index,ab_index | a_index | 5 | NULL | 10 | Using where; Using index |
+----+-------------+-------+-------+------------------+---------+---------+------+------+--------------------------+
1 row in set (0.00 sec)
Query OK, 0 rows affected (0.19 sec)
Records: 0 Duplicates: 0 Warnings: 0
+----+-------------+-------+-------+------------------+----------+---------+------+-------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+------------------+----------+---------+------+-------+--------------------------+
| 1 | SIMPLE | t | range | a_index,ab_index | ab_index | 5 | NULL | 50166 | Using where; Using index |
+----+-------------+-------+-------+------------------+----------+---------+------+-------+--------------------------+
1 row in set (0.00 sec)
这条 SQL 语句之前已经讨论过,当时在没有创建复合索引时,执行的是全表扫描。
而现在,由于创建了复合索引(`ab_index`),查询利用了覆盖索引的特性,从而避免了回表过程。这意味着,MySQL 只需在 `ab_index` 索引上查找,即可获取到所有需要查询的字段。
本文通过几个具体实例,展示了如何使用 `EXPLAIN` 语句来分析 SQL 查询的执行计划,并提到了一些常见的索引优化策略,希望对大家有所帮助。
推荐本站淘宝优惠价购买喜欢的宝贝:
本文链接:https://hqyman.cn/post/5979.html 非本站原创文章欢迎转载,原创文章需保留本站地址!
休息一下~~