MySQL 索引 explain索引分析优化

EXPLAIN是MySQL提供的工具,可用于模拟优化器执行SQL查询语句,从而知道MySQL是如何处理SQL语句的,包括表的读取顺序、数据读取操作的操作类型、可能使用的索引、实际使用的索引、表之间的引用、每张表有多少行数据被优化器查询等。借助于EXPLAIN,可以知道什么时候必须为表加入索引可以得到一个使用索引来寻找记录的更快的SELECT。

eg:

create table tbl_ta(ida int ,txt varchar(20),primary key(ida));

insert into tbl_ta values (1,’AAAAA’);

create table tbl_tb(idb int ,txt varchar(20),primary key(idb));

insert into tbl_tb values (1,’BBBBB’);

create table tbl_tc(idc int ,txt varchar(20),primary key(idc));

insert into tbl_tc values (1,’CCCCC’);

mysql> explain

    -> SELECT tbl_tb.*

    ->   FROM tbl_tb

    ->  WHERE idb =

    ->        (SELECT ida

    ->           FROM tbl_ta

    ->          WHERE ida = (SELECT idc FROM tbl_tc WHERE txt = ‘CCCCC’));

+—-+————-+——–+——-+—————+———+———+——-+——+————-+

| id | select_type | table  | type  | possible_keys | key     | key_len | ref   | rows | Extra       |

+—-+————-+——–+——-+—————+———+———+——-+——+————-+

|  1 | PRIMARY     | tbl_tb | const | PRIMARY       | PRIMARY | 4       | const |    1 | NULL        |

|  2 | SUBQUERY    | tbl_ta | const | PRIMARY       | PRIMARY | 4       | const |    1 | Using index |

|  3 | SUBQUERY    | tbl_tc | ALL   | NULL          | NULL    | NULL    | NULL  |    1 | Using where |

+—-+————-+——–+——-+—————+———+———+——-+——+————-+

3 rows in set (0.00 sec)

mysql> explain extended SELECT tbl_tb.*

    ->   FROM (SELECT idc FROM tbl_tc WHERE txt = ‘CCCCC’) t1, tbl_tb

    ->  WHERE t1.idc = tbl_tb.idb ;

+—-+————-+————+——+—————+————-+———+——————-+——+———-+————-+

| id | select_type | table      | type | possible_keys | key         | key_len | ref               | rows | filtered | Extra       |

+—-+————-+————+——+—————+————-+———+——————-+——+———-+————-+

|  1 | PRIMARY     | tbl_tb     | ALL  | PRIMARY       | NULL        | NULL    | NULL              |    1 |   100.00 | NULL        |

|  1 | PRIMARY     | <derived2> | ref  | <auto_key0>   | <auto_key0> | 4       | sakila.tbl_tb.idb |    2 |   100.00 | Using index |

|  2 | DERIVED     | tbl_tc     | ALL  | NULL          | NULL        | NULL    | NULL              |    1 |   100.00 | Using where |

+—-+————-+————+——+—————+————-+———+——————-+——+———-+————-+

3 rows in set, 1 warning (0.00 sec)

EXPLAIN各字段的业务含义:

项(列明)

可选的值

业务说明

id 查询序列号

1、2….

MySQL Query Optimizer 选定的执行计划中查询的序列号。表示查询中执行 select 子句或操作表的顺序。id 值越大优先级越高,越先被执行,id 相同执行顺序由上至下。

select_type

查询类型

SIMPLE

简单的 select 查询,未使用 union 或子查询

PRIMARY

最外层的 select 查询

UNION

UNION 中的第二个或随后的 select 查询,不依赖于外部查询的结果集

DEPENDENT UNION

UNION 中的第二个或随后的 select 查询,依赖于外部查询的结果集

UNION RESULT

从UNION的结果集select

SUBQUERY

子查询中的第一个 select 查询,不依赖于外部查询的结果集

DEPENDENT SUBQUERY

子查询中的第一个 select 查询,依赖于外部查询的结果集

DERIVED

用于 from 子句里有子查询的情况, MySQL会递归执行这些子查询, 把结果放在临时表里。

UNCACHEABLE SUBQUERY

结果集不能被缓存的子查询,必须重新为外 层查询的每一行进行评估。

UNCACHEABLE UNION

UNION 中的第二个或随后的 select 查询,属于不可缓存的子查询

table

表名

查询中的表名

输出行所引用的表

<derivedX>

匿名子查询结果集(from子句包含子查询)

type

连接使用的类型,按从最优到最差的类型排序

system

表仅有一行(=系统表)。这是 const 连接类型的一个特例。

const

const 用于PRIMARY KEY或UNIQUE索引等值比较时。当查询的表仅有一行时,使用 System。

eq_ref

用于关联查询,从当前表中读取一行,关联的字段是当前表的PRIMARY KEY/UNIQUE索引的等值比较。

ref

连接不能基于关键字选择单个行,可能查找到多个符合条件的行。ref可以用于使用=或<=>操作符的带索引的列。

ref_or_null

如同 ref, 但是 MySQL 必须在初次查找的结果里找出 null 值的行,然后进行二次查找

index_merge

说明索引合并优化被使用了。key列包含了使用的索引的清单,key_len包含了使用的索引的最长的关键元素。

unique_subquery

在某些 IN 查询中使用此种类型,而不是常规的 ref:value IN (SELECT primary_key FROM single_table WHERE some_expr) 。是一个索引查找函数,可以完全替换子查询,效率更高。

index_subquery

在 某 些 IN 查 询 中 使 用 此 种 类 型 , 与 unique_subquery 类似,但是查询的是非唯一性索引: value IN (SELECT key_column FROM single_table WHERE some_expr)

range

只检索给定范围的行,使用一个索引来选择 行。key 列显示使用了哪个索引。当使用=、 <>、>、>=、<、<=、IS NULL、<=>、BETWEEN 或者 IN 操作符,用常量比较关键字列时,可以使用 range。

index

索引全表扫描,只是扫描表的时候按照索引次序进行而不是行。主要优点就是避免了排序, 但是开销仍然非常大。

all

对于每个来自于先前的表的行组合,进行完整的表扫描。

possible_keys

表上已有索引的名称

指出 MySQL 能在该表中使用哪些索引有助于查询。如果为空,说明没有可用的索引。使用覆盖索引,则索引只在key列显示,不在此列显示

key

表上已有索引的名称

MySQL 实际从 possible_key 选择使用的索引。 如果为 NULL,则没有使用索引。很少的情况 下,MYSQL 会选择优化不足的索引。这种情况下,可以在 SELECT 语句中使用 USE INDEX (indexname) force index来强制使用一个索引或者用 IGNORE INDEX(indexname)来强制 MYSQL 忽略索引

key_len

使用索引的长度

使用的索引的长度。在不损失精确性的情况 下,长度越短越好。组合索引的使用情况可以通过索引的长度进行判断。

ref

 

显示索引的哪一列被使用了

rows

 

MYSQL 认为必须检查的用来返回请求数据的行数

 

extra

Distinct

MySQL发现第1个匹配行后,停止为当前的行组合搜索更多的行。

Not exists

MySQL能够对查询进行LEFT JOIN优化,发现1个匹配LEFT JOIN标准的行后,不再为前面的的行组合在该表内检查更多的行。

range checked for each record (index map: #)

MySQL没有发现好的可以使用的索引,但发现如果来自前面的表的列值已知,可能部分索引可以使用。对前面的表的每个行组合,MySQL检查是否可以使用range或index_merge访问方法来索取行。

Using filesort

表示 MySQL 会对结果使用一个外部索引排序,“文件排序”

Using index

使用覆盖索引查找数据。

Using temporary

表示 MySQL 在对查询结果排序时使用临时表。常见于排序 order by 和分组查询 group by。

Using where

从存储引擎获取了结果集数据,表示MySQL服务器将存储引擎返回服务层以后再应用WHERE条件过滤。

Using sort_union(…), Using union(…), Using intersect(…)

这些函数说明如何为index_merge联接类型合并索引扫描。

Using index for group-by

MySQL发现了一个索引,可以用来查询GROUP BY或DISTINCT查询的所有列,而不要额外搜索硬盘访问实际的表。并且,按最有效的方式使用索引,以便对于每个组,只读取少量索引条目。

 

覆盖索引的possible_keys和key情况:

mysql> explain select inventory_id,store_id,film_id from inventory;

+—-+————-+———–+——-+—————+———————-+———+——+——+————-+

| id | select_type | table     | type  | possible_keys | key                  | key_len | ref  | rows | Extra       |

+—-+————-+———–+——-+—————+———————-+———+——+——+————-+

|  1 | SIMPLE      | inventory | index | NULL          | idx_store_id_film_id | 3       | NULL | 4581 | Using index |

+—-+————-+———–+——-+—————+———————-+———+——+——+————-+

1 row in set (0.00 sec)

 

key_len在组合索引中的长度变化

mysql> explain select *  from inventory where store_id=1;

+—-+————-+———–+——+———————-+———————-+———+——-+——+——-+

| id | select_type | table     | type | possible_keys        | key                  | key_len | ref   | rows | Extra |

+—-+————-+———–+——+———————-+———————-+———+——-+——+——-+

|  1 | SIMPLE      | inventory | ref  | idx_store_id_film_id | idx_store_id_film_id | 1       | const | 2269 | NULL  |

+—-+————-+———–+——+———————-+———————-+———+——-+——+——-+

1 row in set (0.01 sec)

 

mysql> explain select *  from inventory where store_id=1 and film_id=1;

+—-+————-+———–+——+————————————-+———————-+———+————-+——+——-+

| id | select_type | table     | type | possible_keys                       | key                  | key_len | ref         | rows | Extra |

+—-+————-+———–+——+————————————-+———————-+———+————-+——+——-+

|  1 | SIMPLE      | inventory | ref  | idx_fk_film_id,idx_store_id_film_id | idx_store_id_film_id | 3       | const,const |    4 | NULL  |

+—-+————-+———–+——+————————————-+———————-+———+————-+——+——-+

1 row in set (0.00 sec)

 

create table t1(id int not null auto_increment,col1 varchar(10),col2 varchar(10),col3 varchar(10), primary key (id), key key_col1_col2_col3(col1,col2,col3));

insert into t1 (col1,col2,col3) values(‘aa’,’ab’,’ac’);

insert into t1 (col1,col2,col3) values(‘ab’,’aa’,’ac’);

insert into t1 (col1,col2,col3) values(‘ac’,’ab’,’aa’);

 

排序和分组字段与索引字段不连续是出现filesort

mysql> explain select col1 from t1 where col1 = ‘aa’  order by col3;

+—-+————-+——-+——+——————–+——————–+———+——-+——+——————————————+

| id | select_type | table | type | possible_keys      | key                | key_len | ref   | rows | Extra                                    |

+—-+————-+——-+——+——————–+——————–+———+——-+——+——————————————+

|  1 | SIMPLE      | t1    | ref  | key_col1_col2_col3 | key_col1_col2_col3 | 33      | const |    1 | Using where; Using index; Using filesort |

+—-+————-+——-+——+——————–+——————–+———+——-+——+——————————————+

1 row in set (0.00 sec)

 

mysql> explain select col1 from t1 where col1 = ‘aa’  order by col2,col3;

+—-+————-+——-+——+——————–+——————–+———+——-+——+————————–+

| id | select_type | table | type | possible_keys      | key                | key_len | ref   | rows | Extra                    |

+—-+————-+——-+——+——————–+——————–+———+——-+——+————————–+

|  1 | SIMPLE      | t1    | ref  | key_col1_col2_col3 | key_col1_col2_col3 | 33      | const |    1 | Using where; Using index |

+—-+————-+——-+——+——————–+——————–+———+——-+——+————————–+

1 row in set (0.00 sec)

 

mysql> explain select col1 from t1 where col1 in ( ‘aa’,’ab’,’ac’)  group by col2;

+—-+————-+——-+——-+——————–+——————–+———+——+——+———————————————————–+

| id | select_type | table | type  | possible_keys      | key                | key_len | ref  | rows | Extra                                                     |

+—-+————-+——-+——-+——————–+——————–+———+——+——+———————————————————–+

|  1 | SIMPLE      | t1    | index | key_col1_col2_col3 | key_col1_col2_col3 | 99      | NULL |    3 | Using where; Using index; Using temporary; Using filesort |

+—-+————-+——-+——-+——————–+——————–+———+——+——+———————————————————–+

1 row in set (0.00 sec)

 

Using where; Using index同时出现表名索引被用来执行索引键值查找(走中间节点找部分索引叶节点)

mysql> explain select col1 from t1 where col1 in ( ‘aa’,’ab’,’ac’)  group by col1,col2;

+—-+————-+——-+——-+——————–+——————–+———+——+——+————————–+

| id | select_type | table | type  | possible_keys      | key                | key_len | ref  | rows | Extra                    |

+—-+————-+——-+——-+——————–+——————–+———+——+——+————————–+

|  1 | SIMPLE      | t1    | index | key_col1_col2_col3 | key_col1_col2_col3 | 99      | NULL |    3 | Using where; Using index |

+—-+————-+——-+——-+——————–+——————–+———+——+——+————————–+

1 row in set (0.00 sec)

 

没有出现Using where表名索引用来读取数据(所有索引数据)

mysql> explain select id,col1,col2 from t1;

+—-+————-+——-+——-+—————+——————–+———+——+——+————-+

| id | select_type | table | type  | possible_keys | key                | key_len | ref  | rows | Extra       |

+—-+————-+——-+——-+—————+——————–+———+——+——+————-+

|  1 | SIMPLE      | t1    | index | NULL          | key_col1_col2_col3 | 99      | NULL |    3 | Using index |

+—-+————-+——-+——-+—————+——————–+———+——+——+————-+

1 row in set (0.00 sec)

 

复杂子查询SQL语句执行过程

mysql> explain select d1.txt, (select idc from tbl_tc) id

    ->   from (select ida id, txt from tbl_ta where txt = ‘AAAAA’) d1

    -> union (select txt, idb id from tbl_tb);

+—-+————–+————+——-+—————+———+———+——+——+—————–+

| id | select_type  | table      | type  | possible_keys | key     | key_len | ref  | rows | Extra           |

+—-+————–+————+——-+—————+———+———+——+——+—————–+

|  1 | PRIMARY      | <derived3> | ALL   | NULL          | NULL    | NULL    | NULL |    2 | NULL            |

|  3 | DERIVED      | tbl_ta     | ALL   | NULL          | NULL    | NULL    | NULL |    1 | Using where     |

|  2 | SUBQUERY     | tbl_tc     | index | NULL          | PRIMARY | 4       | NULL |    1 | Using index     |

|  4 | UNION        | tbl_tb     | ALL   | NULL          | NULL    | NULL    | NULL |    1 | NULL            |

| NULL | UNION RESULT | <union1,4> | ALL   | NULL          | NULL    | NULL    | NULL | NULL | Using temporary |

+—-+————–+————+——-+—————+———+———+——+——+—————–+

5 rows in set (0.00 sec)

 

a、select txt, idb id from tbl_tb

b、select ida id, txt from tbl_ta where txt = ‘AAAAA’

c、select idc from tbl_tc

d、union前最外层的语句

e、合并union结果集

 

order by后的字段与where的字段组合满足最左前缀索引规律

mysql> show index from t1;

+——-+————+——————–+————–+————-+———–+————-+———-+——–+——+————+———+—————+

| Table | Non_unique | Key_name           | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |

+——-+————+——————–+————–+————-+———–+————-+———-+——–+——+————+———+—————+

| t1    |          0 | PRIMARY            |            1 | id          | A         |           3 |     NULL | NULL   |      | BTREE      |         |               |

| t1    |          1 | key_col1_col2_col3 |            1 | col1        | A         |           3 |     NULL | NULL   | YES  | BTREE      |         |               |

| t1    |          1 | key_col1_col2_col3 |            2 | col2        | A         |           3 |     NULL | NULL   | YES  | BTREE      |         |               |

| t1    |          1 | key_col1_col2_col3 |            3 | col3        | A         |           3 |     NULL | NULL   | YES  | BTREE      |         |               |

+——-+————+——————–+————–+————-+———–+————-+———-+——–+——+————+———+—————+

4 rows in set (0.00 sec)

 

mysql> explain select * from t1 where col1=’ac’ and col2>’aa’ order by col3;

+—-+————-+——-+——+——————–+——————–+———+——-+——+——————————————+

| id | select_type | table | type | possible_keys      | key                | key_len | ref   | rows | Extra                                    |

+—-+————-+——-+——+——————–+——————–+———+——-+——+——————————————+

|  1 | SIMPLE      | t1    | ref  | key_col1_col2_col3 | key_col1_col2_col3 | 33      | const |    1 | Using where; Using index; Using filesort |

+—-+————-+——-+——+——————–+——————–+———+——-+——+——————————————+

1 row in set (0.00 sec)

mysql> alter table t1 add index key_col1_col3 (col1,col3);

mysql> alter table t1 drop index key_col1_col2_col3;

mysql> show index from t1;

+——-+————+—————+————–+————-+———–+————-+———-+——–+——+————+———+—————+

| Table | Non_unique | Key_name      | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |

+——-+————+—————+————–+————-+———–+————-+———-+——–+——+————+———+—————+

| t1    |          0 | PRIMARY       |            1 | id          | A         |           3 |     NULL | NULL   |      | BTREE      |         |               |

| t1    |          1 | key_col1_col3 |            1 | col1        | A         |           3 |     NULL | NULL   | YES  | BTREE      |         |               |

| t1    |          1 | key_col1_col3 |            2 | col3        | A         |           3 |     NULL | NULL   | YES  | BTREE      |         |               |

+——-+————+—————+————–+————-+———–+————-+———-+——–+——+————+———+—————+

3 rows in set (0.00 sec)

 

mysql> explain select * from t1 where col1=’ac’ and col2>’aa’ order by col3;

+—-+————-+——-+——+—————+—————+———+——-+——+————————————+

| id | select_type | table | type | possible_keys | key           | key_len | ref   | rows | Extra                              |

+—-+————-+——-+——+—————+—————+———+——-+——+————————————+

|  1 | SIMPLE      | t1    | ref  | key_col1_col3 | key_col1_col3 | 33      | const |    1 | Using index condition; Using where |

+—-+————-+——-+——+—————+—————+———+——-+——+————————————+

1 row in set (0.00 sec)

 

LEFT JOIN左连接,左边的行一定都有,所以右边表是优化关注的重点,右表要尽量使用索引优化(左表建关联字段索引一点意义都没有),右连接一样;

明确指出左右连接后,驱动表即已确定,当通过inner join时,MySQL会选择带索引的表作为被驱动表(后执行)。

CREATE TABLE IF NOT EXISTS classes (

id int(10) unsigned NOT NULL AUTO_INCREMENT,

card int(10) unsigned NOT NULL,

PRIMARY KEY (id)

);

 

CREATE TABLE IF NOT EXISTS book (

bookid int(10) unsigned NOT NULL AUTO_INCREMENT,

card int(10) unsigned NOT NULL,

PRIMARY KEY (bookid)

);

 

mysql> explain select * from classes inner join book on classes.card = book.card;

+—-+————-+———+——+—————+——+———+——+——+—————————————————-+

| id | select_type | table   | type | possible_keys | key  | key_len | ref  | rows | Extra                                              |

+—-+————-+———+——+—————+——+———+——+——+—————————————————-+

|  1 | SIMPLE      | classes | ALL  | NULL          | NULL | NULL    | NULL |    1 | NULL                                               |

|  1 | SIMPLE      | book    | ALL  | NULL          | NULL | NULL    | NULL |    1 | Using where; Using join buffer (Block Nested Loop) |

+—-+————-+———+——+—————+——+———+——+——+—————————————————-+

2 rows in set (0.00 sec)

 

mysql> alter table classes add index  idx_card (card);

Query OK, 0 rows affected (0.01 sec)

Records: 0  Duplicates: 0  Warnings: 0

 

mysql> explain select * from classes inner join book on classes.card = book.card;

+—-+————-+———+——+—————+———-+———+——————+——+————-+

| id | select_type | table   | type | possible_keys | key      | key_len | ref              | rows | Extra       |

+—-+————-+———+——+—————+———-+———+——————+——+————-+

|  1 | SIMPLE      | book    | ALL  | NULL          | NULL     | NULL    | NULL             |    1 | NULL        |

|  1 | SIMPLE      | classes | ref  | idx_card      | idx_card | 4       | sakila.book.card |    1 | Using index |

+—-+————-+———+——+—————+———-+———+——————+——+————-+

2 rows in set (0.00 sec)

 

mysql> drop index idx_card on classes;

Query OK, 0 rows affected (0.01 sec)

Records: 0  Duplicates: 0  Warnings: 0

 

mysql> alter table book add index  idx_card (card);

Query OK, 0 rows affected (0.01 sec)

Records: 0  Duplicates: 0  Warnings: 0

 

mysql> explain select * from classes inner join book on classes.card = book.card;

+—-+————-+———+——+—————+———-+———+———————+——+————-+

| id | select_type | table   | type | possible_keys | key      | key_len | ref                 | rows | Extra       |

+—-+————-+———+——+—————+———-+———+———————+——+————-+

|  1 | SIMPLE      | classes | ALL  | NULL          | NULL     | NULL    | NULL                |    1 | NULL        |

|  1 | SIMPLE      | book    | ref  | idx_card      | idx_card | 4       | sakila.classes.card |    1 | Using index |

+—-+————-+———+——+—————+———-+———+———————+——+————-+

2 rows in set (0.00 sec)

 

 

用小结果集驱动大结果集,即尽可能减少Join语句中NestedLoop的循环次数,同时优化被驱动表的Join索引

JoinBuffer参数的配置

mysql> show variables like ‘%join%’;

+——————+———————-+

| Variable_name    | Value                |

+——————+———————-+

| join_buffer_size | 262144               |

| max_join_size    | 18446744073709551615 |

+——————+———————-+

2 rows in set (0.00 sec)

 

MySQL索引使用注意事项:

1、全索引字段匹配效果最好;

2、最佳左前缀法则;

3、不在索引列上做任何操作(计算、函数、自动或手动类型转换)会导致索引失效,而转向全表扫描;

4、索引范围条件右边的列无法使用索引;

5、尽可能使用覆盖索引,避免使用select *语句;

6、条件中使用不等于(!=或<>)时索引失效;

7、is null 或is not null也会使索引失效;

8、like以统配符开头(%XXX)索引会失效;

9、字符串不加单引号,索引会失效;

10、where子句中使用or连接条件会使索引失效;

 

CREATE TABLE t1 (

  id int(11) NOT NULL AUTO_INCREMENT,

  col1 varchar(10) DEFAULT NULL,

  col3 varchar(10) DEFAULT NULL,

  col4 varchar(50) DEFAULT NULL,

  col5 varchar(10) DEFAULT ‘a5’,

  col2 int(11) DEFAULT ’22’,

  PRIMARY KEY (id),

  KEY idx_col1234 (col1,col2,col3,col4)

);

 

mysql> explain select * from t1 where col1=’aa’;

+—-+————-+——-+——+—————+————-+———+——-+——+———————–+

| id | select_type | table | type | possible_keys | key         | key_len | ref   | rows | Extra                 |

+—-+————-+——-+——+—————+————-+———+——-+——+———————–+

|  1 | SIMPLE      | t1    | ref  | idx_col1234   | idx_col1234 | 33      | const |    1 | Using index condition |

+—-+————-+——-+——+—————+————-+———+——-+——+———————–+

1 row in set (0.00 sec)

 

查询的列覆盖索引

mysql> explain select col1 from t1 where col1=’aa’;

+—-+————-+——-+——+—————+————-+———+——-+——+————————–+

| id | select_type | table | type | possible_keys | key         | key_len | ref   | rows | Extra                    |

+—-+————-+——-+——+—————+————-+———+——-+——+————————–+

|  1 | SIMPLE      | t1    | ref  | idx_col1234   | idx_col1234 | 33      | const |    1 | Using where; Using index |

+—-+————-+——-+——+—————+————-+———+——-+——+————————–+

1 row in set (0.00 sec)

 

col1,col2使用索引

mysql> explain select *  from t1 where col1=’aa’ and col2=22;

+—-+————-+——-+——+—————+————-+———+————-+——+———————–+

| id | select_type | table | type | possible_keys | key         | key_len | ref         | rows | Extra                 |

+—-+————-+——-+——+—————+————-+———+————-+——+———————–+

|  1 | SIMPLE      | t1    | ref  | idx_col1234   | idx_col1234 | 38      | const,const |    1 | Using index condition |

+—-+————-+——-+——+—————+————-+———+————-+——+———————–+

1 row in set (0.00 sec)

where子句中索引列的顺序不影响索引使用情况(能串联)

mysql> explain select *  from t1 where col1=’aa’  and col2=22 and col3=’ab’;

+—-+————-+——-+——+—————+————-+———+——————-+——+———————–+

| id | select_type | table | type | possible_keys | key         | key_len | ref               | rows | Extra                 |

+—-+————-+——-+——+—————+————-+———+——————-+——+———————–+

|  1 | SIMPLE      | t1    | ref  | idx_col1234   | idx_col1234 | 71      | const,const,const |    1 | Using index condition |

+—-+————-+——-+——+—————+————-+———+——————-+——+———————–+

1 row in set (0.00 sec)

mysql> explain select *  from t1 where col1=’aa’ and col3=’ab’  and col2=22;

+—-+————-+——-+——+—————+————-+———+——————-+——+———————–+

| id | select_type | table | type | possible_keys | key         | key_len | ref               | rows | Extra                 |

+—-+————-+——-+——+—————+————-+———+——————-+——+———————–+

|  1 | SIMPLE      | t1    | ref  | idx_col1234   | idx_col1234 | 71      | const,const,const |    1 | Using index condition |

+—-+————-+——-+——+—————+————-+———+——————-+——+———————–+

1 row in set (0.00 sec)

 

范围运算对于数值型,则范围列仍会使用索引,字符型则范围列不会使用索引

mysql> explain select *  from t1 where col1=’aa’  and col2>22 and col3=’ab’;

+—-+————-+——-+——-+—————+————-+———+——+——+———————–+

| id | select_type | table | type  | possible_keys | key         | key_len | ref  | rows | Extra                 |

+—-+————-+——-+——-+—————+————-+———+——+——+———————–+

|  1 | SIMPLE      | t1    | range | idx_col1234   | idx_col1234 | 38      | NULL |    1 | Using index condition |

+—-+————-+——-+——-+—————+————-+———+——+——+———————–+

1 row in set (0.00 sec)

mysql> explain select col1,col2,col3 from t1 where col1=’aa’ and col2>’aa’ and col3=’aa’;

+—-+————-+——-+——+———————+———————+———+——-+——+————————–+

| id | select_type | table | type | possible_keys       | key                 | key_len | ref   | rows | Extra                    |

+—-+————-+——-+——+———————+———————+———+——-+——+————————–+

|  1 | SIMPLE      | t1    | ref  | indx_col1_col2_col3 | indx_col1_col2_col3 | 33      | const |    1 | Using where; Using index |

+—-+————-+——-+——+———————+———————+———+——-+——+————————–+

1 row in set (0.00 sec)

注:col1单列的索引长度即为33

 

mysql> explain select *  from t1 where col1=’aa’  and col2=22 and col3=’ab’ and col4=’a4′;

+—-+————-+——-+——+—————+————-+———+————————-+——+———————–+

| id | select_type | table | type | possible_keys | key         | key_len | ref                     | rows | Extra                 |

+—-+————-+——-+——+—————+————-+———+————————-+——+———————–+

|  1 | SIMPLE      | t1    | ref  | idx_col1234   | idx_col1234 | 224     | const,const,const,const |    1 | Using index condition |

+—-+————-+——-+——+—————+————-+———+————————-+——+———————–+

1 row in set (0.00 sec)

索引列顺序不影响索引使用

mysql> explain select *  from t1 where col1=’aa’  and col2=22 and col4=’a4′ and col3=’ab’;

+—-+————-+——-+——+—————+————-+———+————————-+——+———————–+

| id | select_type | table | type | possible_keys | key         | key_len | ref                     | rows | Extra                 |

+—-+————-+——-+——+—————+————-+———+————————-+——+———————–+

|  1 | SIMPLE      | t1    | ref  | idx_col1234   | idx_col1234 | 224     | const,const,const,const |    1 | Using index condition |

+—-+————-+——-+——+—————+————-+———+————————-+——+———————–+

1 row in set (0.00 sec)

col1、col2使用索引

mysql> explain select *  from t1 where col1=’aa’  and col2=22 and col3>’ab’ and col4=’a4′;

+—-+————-+——-+——-+—————+————-+———+——+——+———————–+

| id | select_type | table | type  | possible_keys | key         | key_len | ref  | rows | Extra                 |

+—-+————-+——-+——-+—————+————-+———+——+——+———————–+

|  1 | SIMPLE      | t1    | range | idx_col1234   | idx_col1234 | 71      | NULL |    1 | Using index condition |

+—-+————-+——-+——-+—————+————-+———+——+——+———————–+

1 row in set (0.00 sec)

col1、col2、col3、col4使用索引

mysql> explain select *  from t1 where col1=’aa’  and col2=22 and col4>’a4′ and col3=’ab’;

+—-+————-+——-+——-+—————+————-+———+——+——+———————–+

| id | select_type | table | type  | possible_keys | key         | key_len | ref  | rows | Extra                 |

+—-+————-+——-+——-+—————+————-+———+——+——+———————–+

|  1 | SIMPLE      | t1    | range | idx_col1234   | idx_col1234 | 224     | NULL |    1 | Using index condition |

+—-+————-+——-+——-+—————+————-+———+——+——+———————–+

1 row in set (0.00 sec)

 

order by字段与where子句的字段组合能连起来,满足最左前缀法则,col3使用索引排序

mysql> explain select *  from t1 where col1=’aa’  and col2=22 and col4=’a4′ order by  col3;

+—-+————-+——-+——+—————+————-+———+————-+——+————————————+

| id | select_type | table | type | possible_keys | key         | key_len | ref         | rows | Extra                              |

+—-+————-+——-+——+—————+————-+———+————-+——+————————————+

|  1 | SIMPLE      | t1    | ref  | idx_col1234   | idx_col1234 | 38      | const,const |    1 | Using index condition; Using where |

+—-+————-+——-+——+—————+————-+———+————-+——+————————————+

1 row in set (0.00 sec)

mysql> explain select *  from t1 where col1=’aa’  and col2=22  order by  col3;

+—-+————-+——-+——+—————+————-+———+————-+——+————————————+

| id | select_type | table | type | possible_keys | key         | key_len | ref         | rows | Extra                              |

+—-+————-+——-+——+—————+————-+———+————-+——+————————————+

|  1 | SIMPLE      | t1    | ref  | idx_col1234   | idx_col1234 | 38      | const,const |    1 | Using index condition; Using where |

+—-+————-+——-+——+—————+————-+———+————-+——+————————————+

1 row in set (0.00 sec)

mysql> explain select *  from t1 where col1=’aa’  and col2=22  order by  col4;

+—-+————-+——-+——+—————+————-+———+————-+——+—————————————————-+

| id | select_type | table | type | possible_keys | key         | key_len | ref         | rows | Extra                                              |

+—-+————-+——-+——+—————+————-+———+————-+——+—————————————————-+

|  1 | SIMPLE      | t1    | ref  | idx_col1234   | idx_col1234 | 38      | const,const |    1 | Using index condition; Using where; Using filesort |

+—-+————-+——-+——+—————+————-+———+————-+——+—————————————————-+

1 row in set (0.00 sec)

 

order by子句字段与where子句字段组合,且要与索引字段顺序一致

mysql> explain select *  from t1 where col1=’aa’  and col5=’a5′  order by  col2,col3;

+—-+————-+——-+——+—————+————-+———+——-+——+————————————+

| id | select_type | table | type | possible_keys | key         | key_len | ref   | rows | Extra                              |

+—-+————-+——-+——+—————+————-+———+——-+——+————————————+

|  1 | SIMPLE      | t1    | ref  | idx_col1234   | idx_col1234 | 33      | const |    1 | Using index condition; Using where |

+—-+————-+——-+——+—————+————-+———+——-+——+————————————+

1 row in set (0.00 sec)

mysql> explain select *  from t1 where col1=’aa’  and col5=’a5′  order by  col3,col2;

+—-+————-+——-+——+—————+————-+———+——-+——+—————————————————-+

| id | select_type | table | type | possible_keys | key         | key_len | ref   | rows | Extra                                              |

+—-+————-+——-+——+—————+————-+———+——-+——+—————————————————-+

|  1 | SIMPLE      | t1    | ref  | idx_col1234   | idx_col1234 | 33      | const |    1 | Using index condition; Using where; Using filesort |

+—-+————-+——-+——+—————+————-+———+——-+——+—————————————————-+

1 row in set (0.00 sec)

 

where子句包含了col1、col2与order by中的字段可以组合满足最左前缀法则,同时where加入的字段不影响order by索引的使用

mysql> explain select *  from t1 where col1=’aa’  and col2=22  order by  col2,col3;

+—-+————-+——-+——+—————+————-+———+————-+——+————————————+

| id | select_type | table | type | possible_keys | key         | key_len | ref         | rows | Extra                              |

+—-+————-+——-+——+—————+————-+———+————-+——+————————————+

|  1 | SIMPLE      | t1    | ref  | idx_col1234   | idx_col1234 | 38      | const,const |    1 | Using index condition; Using where |

+—-+————-+——-+——+—————+————-+———+————-+——+————————————+

1 row in set (0.00 sec)

mysql> explain select *  from t1 where col1=’aa’  and col2=22  order by  col3,col2;

+—-+————-+——-+——+—————+————-+———+————-+——+————————————+

| id | select_type | table | type | possible_keys | key         | key_len | ref         | rows | Extra                              |

+—-+————-+——-+——+—————+————-+———+————-+——+————————————+

|  1 | SIMPLE      | t1    | ref  | idx_col1234   | idx_col1234 | 38      | const,const |    1 | Using index condition; Using where |

+—-+————-+——-+——+—————+————-+———+————-+——+————————————+

1 row in set (0.00 sec)

mysql> explain select *  from t1 where col1=’aa’  and col2=22  and col5=’a5′  order by  col2,col3;

+—-+————-+——-+——+—————+————-+———+————-+——+————————————+

| id | select_type | table | type | possible_keys | key         | key_len | ref         | rows | Extra                              |

+—-+————-+——-+——+—————+————-+———+————-+——+————————————+

|  1 | SIMPLE      | t1    | ref  | idx_col1234   | idx_col1234 | 38      | const,const |    1 | Using index condition; Using where |

+—-+————-+——-+——+—————+————-+———+————-+——+————————————+

1 row in set (0.00 sec)

mysql> explain select *  from t1 where col1=’aa’  and col2=22  and col5=’a5′  order by  col3,col2;

+—-+————-+——-+——+—————+————-+———+————-+——+————————————+

| id | select_type | table | type | possible_keys | key         | key_len | ref         | rows | Extra                              |

+—-+————-+——-+——+—————+————-+———+————-+——+————————————+

|  1 | SIMPLE      | t1    | ref  | idx_col1234   | idx_col1234 | 38      | const,const |    1 | Using index condition; Using where |

+—-+————-+——-+——+—————+————-+———+————-+——+————————————+

1 row in set (0.00 sec)

 

group by未使用索引时会增加Using temporary和Using filesort其他与order by规律一致

mysql> explain select *  from t1 where col1=’aa’  and col4=’a4′  group  by  col2,col3;

+—-+————-+——-+——+—————+————-+———+——-+——+————————————+

| id | select_type | table | type | possible_keys | key         | key_len | ref   | rows | Extra                              |

+—-+————-+——-+——+—————+————-+———+——-+——+————————————+

|  1 | SIMPLE      | t1    | ref  | idx_col1234   | idx_col1234 | 33      | const |    1 | Using index condition; Using where |

+—-+————-+——-+——+—————+————-+———+——-+——+————————————+

1 row in set (0.00 sec)

mysql> explain select *  from t1 where col1=’aa’  and col4=’a4′  group  by  col3,col2;

+—-+————-+——-+——+—————+————-+———+——-+——+———————————————————————+

| id | select_type | table | type | possible_keys | key         | key_len | ref   | rows | Extra                                                               |

+—-+————-+——-+——+—————+————-+———+——-+——+———————————————————————+

|  1 | SIMPLE      | t1    | ref  | idx_col1234   | idx_col1234 | 33      | const |    1 | Using index condition; Using where; Using temporary; Using filesort |

+—-+————-+——-+——+—————+————-+———+——-+——+———————————————————————+

1 row in set (0.00 sec)

mysql> explain select *  from t1 where col1=’aa’ and col2=22  and col4=’a4′  group  by  col3,col2;

+—-+————-+——-+——+—————+————-+———+————-+——+————————————+

| id | select_type | table | type | possible_keys | key         | key_len | ref         | rows | Extra                              |

+—-+————-+——-+——+—————+————-+———+————-+——+————————————+

|  1 | SIMPLE      | t1    | ref  | idx_col1234   | idx_col1234 | 38      | const,const |    1 | Using index condition; Using where |

+—-+————-+——-+——+—————+————-+———+————-+——+————————————+

1 row in set (0.00 sec)

 

like右统配不影响后续索引的使用

mysql> explain select *  from t1 where col1=’aa’ and col2=22  and col3 like ‘a%’ and col4=’a4′;

+—-+————-+——-+——-+—————+————-+———+——+——+———————–+

| id | select_type | table | type  | possible_keys | key         | key_len | ref  | rows | Extra                 |

+—-+————-+——-+——-+—————+————-+———+——+——+———————–+

|  1 | SIMPLE      | t1    | range | idx_col1234   | idx_col1234 | 224     | NULL |    1 | Using index condition |

+—-+————-+——-+——-+—————+————-+———+——+——+———————–+

1 row in set (0.00 sec)

mysql> explain select *  from t1 where col1=’aa’ and col2=22  and col3 like ‘a%’;

+—-+————-+——-+——-+—————+————-+———+——+——+———————–+

| id | select_type | table | type  | possible_keys | key         | key_len | ref  | rows | Extra                 |

+—-+————-+——-+——-+—————+————-+———+——+——+———————–+

|  1 | SIMPLE      | t1    | range | idx_col1234   | idx_col1234 | 71      | NULL |    1 | Using index condition |

+—-+————-+——-+——-+—————+————-+———+——+——+———————–+

1 row in set (0.00 sec)

mysql> explain select *  from t1 where col1=’aa’ and col2=22  and col3 like ‘%a’ and col4=’a4′;

+—-+————-+——-+——+—————+————-+———+————-+——+———————–+

| id | select_type | table | type | possible_keys | key         | key_len | ref         | rows | Extra                 |

+—-+————-+——-+——+—————+————-+———+————-+——+———————–+

|  1 | SIMPLE      | t1    | ref  | idx_col1234   | idx_col1234 | 38      | const,const |    1 | Using index condition |

+—-+————-+——-+——+—————+————-+———+————-+——+———————–+

1 row in set (0.00 sec)

mysql> explain select *  from t1 where col1=’aa’ and col2=22  and col3 like ‘%a%’ and col4=’a4′;

+—-+————-+——-+——+—————+————-+———+————-+——+———————–+

| id | select_type | table | type | possible_keys | key         | key_len | ref         | rows | Extra                 |

+—-+————-+——-+——+—————+————-+———+————-+——+———————–+

|  1 | SIMPLE      | t1    | ref  | idx_col1234   | idx_col1234 | 38      | const,const |    1 | Using index condition |

+—-+————-+——-+——+—————+————-+———+————-+——+———————–+

1 row in set (0.00 sec)

mysql> explain select *  from t1 where col1=’aa’ and col2=22  and col3 like ‘a%a%’ and col4=’a4′;

+—-+————-+——-+——-+—————+————-+———+——+——+———————–+

| id | select_type | table | type  | possible_keys | key         | key_len | ref  | rows | Extra                 |

+—-+————-+——-+——-+—————+————-+———+——+——+———————–+

|  1 | SIMPLE      | t1    | range | idx_col1234   | idx_col1234 | 224     | NULL |    1 | Using index condition |

+—-+————-+——-+——-+—————+————-+———+——+——+———————–+

1 row in set (0.00 sec)

 

 

 

MySQL删除列,会自动将该列的组合索引中该列也删除;

 

rows是核心指标,绝大部分rows小的语句执行一定很快(有例外,下面会讲到)。所以优化语句基本上都是在优化rows。

 

建索引的几大原则

1.最左前缀匹配原则,非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。

2.=和in可以乱序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意顺序,mysql的查询优化器会帮你优化成索引可以识别的形式

3.尽量选择区分度高的列作为索引,区分度的公式是count(distinct col)/count(*),表示字段不重复的比例,比例越大我们扫描的记录数越少,唯一键的区分度是1,而一些状态、性别字段可能在大数据面前区分度就是0,那可能有人会问,这个比例有什么经验值吗?使用场景不同,这个值也很难确定,一般需要join的字段我们都要求是0.1以上,即平均1条扫描10条记录

4.索引列不能参与计算,保持列“干净”,比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,原因很简单,b+树中存的都是数据表中的字段值,但进行检索时,需要把所有元素都应用函数才能比较,显然成本太大。所以语句应该写成create_time = unix_timestamp(’2014-05-29’);

5.尽量的扩展索引,不要新建索引。比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可

rows是核心指标,绝大部分rows小的语句执行一定很快(有例外,下面会讲到)。所以优化语句基本上都是在优化rows。

 

参考资料:

http://database.51cto.com/art/201108/285341.htm

 

    原文作者:mysql索引优化
    原文地址: https://my.oschina.net/peakfang/blog/2875289
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞