如何分析一条sql的性能?( 二 )


| 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)
mysql> explain select * from t where a > 1000;
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
| 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 竟然显示刚刚不是给字段 a 添加索引了么,而且 possible_keys 也显示了有 a_index 可用,但是 key 显示 null,表示 mysql 实际上并不会使用 a 索引,这是为啥?
这里是因为 select * 的话还需要回到主键索引上查找 b 字段,这个过程叫回表,这条语句会筛选出 9w 条满足条件的数据,也就是说这 9w 条数据都需要回表操作,全表扫描都才 10w 条数据,所以在 mysql 的优化器看来还不如直接全表扫描得了,至少还免去了回表过程了 。
当然也不是说只要有回表操作就不会命中索引,用不用索引关键还在于 mysql 认为哪种查询代价更低,我们把上面的 sql 中 where 条件再稍微改造一下 。
mysql> explain select * from t where a > 99000;+----+-------------+-------+-------+---------------+---------+---------+------+------+-----------------------+| 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 条数据就算回表也要比全表扫描的代价低,所以说 mysql 其实是个很聪明的家伙 。
我们还可以看到 Extra 字段中值为 Using index condition,这个意思是指用到了索引,但是需要回表,再看下面这个语句 。
mysql> explain select a from t where a > 99000;+----+-------------+-------+-------+---------------+---------+---------+------+------+--------------------------+| 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)
这个 Extra 中的值为 Using where; Using index ,表示查询用到了索引,且要查询的字段在索引中就能拿到,不需要回表,显然这种效率比上面的要高,所以不要轻易写 select * ,只查询业务需要的字段即可,这样可以尽可能避免回表 。
再来看一个需要排序的 。
mysql> explain select a from t where a > 99000 order by b;
+----+-------------+-------+-------+---------------+---------+---------+------+------+---------------------------------------+
| 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)这个 Extra 中返回了一个 Using filesort,意味着需要排序,这种是需要重点优化的的,也就是说查到数据后,还需要 mysql 在内存中对其进行排序,你要知道索引本身就是有序的,所以一般来讲要尽量利用索引的有序性,比如像下面这样写 。


推荐阅读