mysql调优——sql优化(3):利用覆盖索引优化查询

概念简介

  什么是覆盖索引?

  就是select的数据列只用从索引中就能够取得,不必读取数据行,换句话说查询列要被所建的索引覆盖。使用覆盖索引要求:select后面的列在索引中。

 

  举一个例子,我们知道mysql中的模糊查询是不会走索引的:

  query 1,模糊查询:

  > explain select sql_no_cache * from 表A where 列a LIKE ‘%兽医%‘ limit 10; 

+----+-------------+---------+------+---------------+------+---------+------+--------+-------------+
| id | select_type | table   | type | possible_keys | key  | key_len | ref  | rows   | Extra       |
+----+-------------+---------+------+---------------+------+---------+------+--------+-------------+
|  1 | SIMPLE      | 表A     | ALL  | NULL          | NULL | NULL    | NULL | 748521 | Using where |
+----+-------------+---------+------+---------------+------+---------+------+--------+-------------+

  query 2,模糊查询,覆盖索引:

  > explain select sql_no_cache id from 表A where 列a LIKE ‘%兽医%‘ limit 10;

+----+-------------+---------+-------+---------------+-----------+---------+------+--------+--------------------------+
| id | select_type | table   | type  | possible_keys | key       | key_len | ref  | rows   | Extra                    |
+----+-------------+---------+-------+---------------+-----------+---------+------+--------+--------------------------+
|  1 | SIMPLE      | 表A     | index | NULL          | doc_title | 767     | NULL | 748521 | Using where; Using index |
+----+-------------+---------+-------+---------------+-----------+---------+------+--------+--------------------------+

  同样的查询条件,只因为查询的内容不一样,就利用到了索引,原因就是,检索只在索引中进行;

  注意这里的说法:检索只在索引中进行,而不是说走索引检索数据;这一点在查询计划里也有体现,possible_keys的值是null,但是key的值是doc_title。

 

  那么为什么会在数据里检索,只在索引里检索?

  简单说,索引的数据足够详细了,就不用去数据里找了。详细的内容需要去了解,statement在mysql中的运行过程,这里暂时先不做赘述。可以通过profile先对sql的执行过程有一个简单了解。

 

 

待优化sql:SELECT * FROM 表A as t WHERE title LIKE ‘%兽医%‘ AND category=‘3‘ ORDER BY t.bd_rank LIMIT 50;

查询计划:

+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
|  1 | SIMPLE      | t     | index | category      | bd_rank | 2       | NULL |  193 | Using where |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+

表中的索引状况,为了排版好看起见,我把为空的列暂时先删除:

> show index from 表A;

+-------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------------+---------+
| Table | Non_unique | Key_name  | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Index_type | Comment |
+-------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------------+---------+
|   表A |          0 | PRIMARY   |            1 | id          | A         |      769962 |     NULL | NULL   | BTREE      |         |
|   表A |          1 | idx_title |            1 | title       | A         |      769962 |     NULL | NULL   | BTREE      |         |
|   表A |          1 | category  |            1 | category    | A         |         150 |     NULL | NULL   | BTREE      |         |
|   表A |          1 | bd_rank   |            1 | bd_rank     | A         |         481 |     NULL | NULL   | BTREE      |         |
+-------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------------+---------+

对于这张表来说,索引创建还是比较合理的;注意到以下几点:

 (1)走了bd_rank索引而没有走category索引,为什么?mysql为什么这样选?

    这个受cardinality列的影响。

    cardinality值表示该列唯一值得个数,这个值并不准确,比如(2)的解答。这个值越大,就越容易选择这个列的索引。

    有没有人问为什么不选cardinality值最大的列?哈哈,自己想想。

    具体请参考 http://www.mysqlperformanceblog.com/2008/09/03/analyze-myisam-vs-innodb/

 (2)能不能用category和bd_rank建一个联合索引,会不会有更好的效果?

    我们来查查category值得分布,发现只有4种值,因此不用建联合索引。

 

  那么以上就是我们需要优化的sql对象了,我们排除了创建联合索引,弄清楚了为何走bd_rank索引,只剩下模糊查询带来的问题了。

  用覆盖索引对这条sql做修改,如下:

  > select t.* from se_rank t join (select id from se_rank where doc_title LIKE ‘%兽医%‘ and category=3) s on t.id=s.id ORDER BY `t`.`bd_rank` LIMIT 50;

  注意:把条件category=3放在了括号内,想想有什么好处?

  

  改完之后,对于这个关键字来讲,查询效率提高了8倍;

  看看具体的查询计划:

+----+-------------+------------+--------+---------------+----------+---------+------+--------+---------------------------------+
| id | select_type | table      | type   | possible_keys | key      | key_len | ref  | rows   | Extra                           |
+----+-------------+------------+--------+---------------+----------+---------+------+--------+---------------------------------+
|  1 | PRIMARY     | <derived2> | ALL    | NULL          | NULL     | NULL    | NULL |      9 | Using temporary; Using filesort |
|  1 | PRIMARY     | t          | eq_ref | PRIMARY       | PRIMARY  | 4       | s.id |      1 |                                 |
|  2 | DERIVED     | se_rank    | ref    | category      | category | 2       |      | 193802 | Using where                     |
+----+-------------+------------+--------+---------------+----------+---------+------+--------+---------------------------------+

  发现优化后的查询造成了temporary表,用到了filesort,我在其他文章中也讲过,我们要避免这两类。但是现在很奇怪,这也是没办法,我们还是要极力避免这两类,但是也要视情况决定。

  为什么提高了8倍,我自己做了简单分析:(1)是用到了覆盖索引(2)是把category=3放在了括号内,效果体现在derived2中;

  

思考:

  (1)覆盖索引为什么效率会更快?

      这和innodb的btree结构有关,innodb中,索引和数据在一起,都作为b树的叶子节点;

  (2)mysql是先在index中搜索,然后在数据中搜索吗?

      建议去看一看简朝阳的《MySQL性能调优与架构设计

  

  

  

 

  

mysql调优——sql优化(3):利用覆盖索引优化查询,古老的榕树,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。