mysql聚合索引失效情况讨论

1、创建测试环境

创建student表进行测试,将其中三列创建索引,创建表语句如下所示:

mysql> create table student(
    -> id int(10)  NOT NULL AUTO_INCREMENT COMMENT 'ID',
    -> name varchar(10) NOT NULL ,
    -> passworld varchar(10) NOT NULL ,
    -> age int(11) not null,
    -> sex varchar(1) not null,
    -> primary key(id),
    -> key `ind_stu` (`name`,`passworld`,`age`)
    -> )
    -> ;
Query OK, 0 rows affected, 2 warnings (0.04 sec)

插入数据:

mysql> insert into student values(1,'zhangsan','123456','18','1');
Query OK, 1 row affected (0.00 sec)

mysql> insert into student values(2,'lisi','123456','18','2');
Query OK, 1 row affected (0.01 sec)

mysql> insert into student values(3,'admin','123456','18','2');
Query OK, 1 row affected (0.01 sec)

结果查看:

mysql> select *from student;
+----+----------+-----------+-----+-----+
| id | name     | passworld | age | sex |
+----+----------+-----------+-----+-----+
|  1 | zhangsan | 123456    |  18 | 1   |
|  2 | lisi     | 123456    |  18 | 2   |
|  3 | admin    | 123456    |  18 | 2   |
+----+----------+-----------+-----+-----+
3 rows in set (0.01 sec)

2、explain语句简介

  • explain:在select语句之前增加explain关键字,执行后MySQL就会返回执行计划的信息,而不是执行sql。但如果from中包含子查询,MySQL仍会执行该子查询,并把子查询的结果放入临时表中。
mysql> explain select * from student;
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------+
| id | select_type | table   | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------+
|  1 | SIMPLE      | student | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    3 |   100.00 | NULL  |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------+
1 row in set, 1 warning (0.01 sec)
  • id列 :
    id列的编号是select的序列号,有几个select就有几个id,并且id是按照select出现的顺序增长的,id列的值越大优先级越高,id相同则是按照执行计划列从上往下执行,id为空则是最后执行。
  • select_type列:
    表示对应行是简单查询还是复杂查询。

1、simple:不包含子查询和union的简单查询
2、primary:复杂查询中最外层的select
3、subquery:包含在select中的子查询(不在from的子句中)

  • table列:
    表示当前行访问的是哪张表。

  • partitions列:
    查询将匹配记录的分区。 对于非分区表,该值为 NULL

  • type列:
    此列表示关联类型或访问类型。也就是MySQL决定如何查找表中的行。依次从最优到最差分别为:system > const > eq_ref > ref > range > index > all。

  • possible_keys :
    此列显示在查询中可能用到的索引。

  • key 列:
    此列显示MySQL在查询时实际用到的索引。在执行计划中可能出现possible_keys列有值,而key列为null,这种情况可能是表中数据不多,MySQL认为索引对当前查询帮助不大而选择了全表查询。

  • key_len 列:
    此列显示MySQL在索引里使用的字节数,通过此列可以算出具体使用了索引中的那些列。

  • ref列:
    此列显示key列记录的索引中,表查找值时使用到的列或常量。常见的有const、字段名

  • rows 列:
    此列是MySQL在查询中估计要读取的行数。注意这里不是结果集的行数。

  • filtered 列:
    此列是一些额外信息。常见的重要值如下:

              1)Using index:使用覆盖索引(如果select后面查询的字段都可以从这个索引的树中获取,不需要通过辅助索引树找到主键,再通过主键去主键索引树里获取其它字段值,这种情况一般可以说是用到了覆盖索引)。
    
              2)Using where:使用 where 语句来处理结果,并且查询的列未被索引覆盖。
    
              3)Using index condition:查询的列不完全被索引覆盖,where条件中是一个查询的范围。
    
              4)Using temporary:MySQL需要创建一张临时表来处理查询。出现这种情况一般是要进行优化的。
    
              5)Using filesort:将使用外部排序而不是索引排序,数据较小时从内存排序,否则需要在磁盘完成排序。
    
              6)Select tables optimized away:使用某些聚合函数(比如 max、min)来访问存在索引的某个字段时。
    

3、验证

(1):全列查询、顺序不同

情况一:正常查询:使用 ind_stu (name,passworld,age)

mysql> explain select * from student where name = 'admin' and passworld = '123456' and age = '18';
+----+-------------+---------+------------+------+---------------+---------+---------+-------------------+------+----------+-------+
| id | select_type | table   | partitions | type | possible_keys | key     | key_len | ref               | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+---------+---------+-------------------+------+----------+-------+
|  1 | SIMPLE      | student | NULL       | ref  | ind_stu       | ind_stu | 88      | const,const,const |    1 |   100.00 | NULL  |
+----+-------------+---------+------------+------+---------------+---------+---------+-------------------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

结论一:使用name 、passworld、age、进行查询可以命中索引。

情况二:将查询顺序改为name 、age、password
explain select * from student where name = 'admin' and age = '18' and passworld ='123456';

+----+-------------+---------+------------+------+---------------+---------+---------+-------------------+------+----------+-------+
| id | select_type | table   | partitions | type | possible_keys | key     | key_len | ref               | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+---------+---------+-------------------+------+----------+-------+
|  1 | SIMPLE      | student | NULL       | ref  | ind_stu       | ind_stu | 88      | const,const,const |    1 |   100.00 | NULL  |
+----+-------------+---------+------------+------+---------------+---------+---------+-------------------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
结论二:顺序为name、age、passworld时依旧可以命中索引。

情况三:将查询顺序改为password、name、age
查询语句:mysql> explain select * from student where passworld = '123456' and name ='admin' and age = '18';

+----+-------------+---------+------------+------+---------------+---------+---------+-------------------+------+----------+-------+
| id | select_type | table   | partitions | type | possible_keys | key     | key_len | ref               | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+---------+---------+-------------------+------+----------+-------+
|  1 | SIMPLE      | student | NULL       | ref  | ind_stu       | ind_stu | 88      | const,const,const |    1 |   100.00 | NULL  |
+----+-------------+---------+------------+------+---------------+---------+---------+-------------------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
结论三:将查询顺序改为password、name、age可以命中索引

情况四:将查询顺序改为age、name、passworld
查询语句:mysql> explain select * from student where age = '18' and name = 'admin' and passworld = '123456';

+----+-------------+---------+------------+------+---------------+---------+---------+-------------------+------+----------+-------+
| id | select_type | table   | partitions | type | possible_keys | key     | key_len | ref               | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+---------+---------+-------------------+------+----------+-------+
|  1 | SIMPLE      | student | NULL       | ref  | ind_stu       | ind_stu | 88      | const,const,const |    1 |   100.00 | NULL  |
+----+-------------+---------+------------+------+---------------+---------+---------+-------------------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

将查询顺序改为age、name、passworld可以命中索引。

  • 因本文篇幅原因:三列组成聚合索引,其他查询顺序情况不继续展示。直接在下方给出结论:
总结一;由上述可知在用聚合索引的全部列组成条件进行查询时,使用and连接时,列的顺序不会产生影响,即都可以命中索引。

2、聚合索引部分列查询

情况五:使用name 、passworld查询:
查询语句: explain select * from student where name = 'admin' and passworld = '123456'

+----+-------------+---------+------------+------+---------------+---------+---------+-------------+------+----------+-------+
| id | select_type | table   | partitions | type | possible_keys | key     | key_len | ref         | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+---------+---------+-------------+------+----------+-------+
|  1 | SIMPLE      | student | NULL       | ref  | ind_stu       | ind_stu | 84      | const,const |    1 |   100.00 | NULL  |
+----+-------------+---------+------------+------+---------------+---------+---------+-------------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

结论五:使用name 、passworld查询可以命中索引。

情况六:使用name、age查询
查询语句:mysql> explain select * from student where name = 'admin' and age = '18';

+----+-------------+---------+------------+------+---------------+---------+---------+-------+------+----------+-----------------------+
| id | select_type | table   | partitions | type | possible_keys | key     | key_len | ref   | rows | filtered | Extra                 |
+----+-------------+---------+------------+------+---------------+---------+---------+-------+------+----------+-----------------------+
|  1 | SIMPLE      | student | NULL       | ref  | ind_stu       | ind_stu | 42      | const |    1 |    33.33 | Using index condition |
+----+-------------+---------+------------+------+---------------+---------+---------+-------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)

结论七:使用name、age查询可以命中索引。

提示重点来了:
情况八:使用passworld、age查询
查询语句: explain select * from student where passworld = '123456' and age = '18';

+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table   | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | student | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    3 |    33.33 | Using where |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
* 结论八:使用passworld、age查询不能命中索引 *

结论七使用部分列都可以通过索引,情况八为什么不可以?我们继续看情况九
情况九:使用age、passworld查询
查询语句:mysql> explain select * from student where age = '18' and passworld = '123456' ;

+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table   | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | student | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    3 |    33.33 | Using where |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
结论九:由上述情况可知,在使用部分列查询时,影响索引命中的是创建索引时,最左列。那么最左列的顺序是否对索引命中产生影响?看情况十。

情况十:使用passworld、name进行查询:
查询语句:explain select * from student where passworld='123456'and name = 'admin';

+----+-------------+---------+------------+------+---------------+---------+---------+-------------+------+----------+-------+
| id | select_type | table   | partitions | type | possible_keys | key     | key_len | ref         | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+---------+---------+-------------+------+----------+-------+
|  1 | SIMPLE      | student | NULL       | ref  | ind_stu       | ind_stu | 84      | const,const |    1 |   100.00 | NULL  |
+----+-------------+---------+------------+------+---------------+---------+---------+-------------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
结论十:使用部分列进行查询时,在最左列存在时,与顺序无关。可命中索引。

3、使用聚合索引部分列和非索引列查询

情况十一:使用聚合索引和非索引列
查询语句:explain select * from student where name ='admin' and passworld = '123456' and age ='18' and sex ='1';

+----+-------------+---------+------------+------+---------------+---------+---------+-------------------+------+----------+-------------+
| id | select_type | table   | partitions | type | possible_keys | key     | key_len | ref               | rows | filtered | Extra       |
+----+-------------+---------+------------+------+---------------+---------+---------+-------------------+------+----------+-------------+
|  1 | SIMPLE      | student | NULL       | ref  | ind_stu       | ind_stu | 88      | const,const,const |    1 |    33.33 | Using where |
+----+-------------+---------+------------+------+---------------+---------+---------+-------------------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
结论十一:使用聚合索引和非索引列可命中索引。

情况十二:使用name和sex查询

mysql> explain select * from student where name ='admin' and sex ='1';
+----+-------------+---------+------------+------+---------------+---------+---------+-------+------+----------+-------------+
| id | select_type | table   | partitions | type | possible_keys | key     | key_len | ref   | rows | filtered | Extra       |
+----+-------------+---------+------------+------+---------------+---------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | student | NULL       | ref  | ind_stu       | ind_stu | 42      | const |    1 |    33.33 | Using where |
+----+-------------+---------+------------+------+---------------+---------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
结论十二:使用最左列和非索引列可命中索引。

情况十三:使用非最左列和非索引列

mysql> explain select * from student where passworld = '123456' and sex ='1';
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table   | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | student | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    3 |    33.33 | Using where |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
```shell

mysql> explain select * from student where age = '18' and sex ='1';
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table   | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | student | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    3 |    33.33 | Using where |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
mysql> explain select * from student where passworld = '123456'and age = '18' and sex ='1';
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table   | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+---------+------------+------+---------------+------+---------+-----
```-+------+----------+-------------+
|  1 | SIMPLE      | student | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    3 |    33.33 | Using where |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

结论十三:使用非最左列和非索引列全表扫描。

总结:
     1、在使用聚合索引查询时,顺序不会产生印象,索引会生效。
     2、在使用聚合索引+非索引列时,索引生效。
     3、在使用部分聚合索引列时,最左列存在时,与顺序无关,索引生效。
     4、在使用部分聚合索引列时,最左列不存在时,与顺序无关索引失效。
     5、在使用部分聚合索引+非索引列时,最左列存在时,与顺序无关,索引生效。
     6、在使用部分聚合索引+非索引列时,最左列不存在时,与顺序无关,索引失效。
       
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
场景一: 确认1.碰到了一个一个非常慢的SQL server语句,发现是变量查询时很慢 SQL语句: DECLARE @SN VARCHAR(12) SET @SN = '30F335CD0045' SELECT [Mac2] FROM SF_Cp_Detail WHERE [Mac2] = @SN 确认2.查看索引是:唯一非聚集索引 CREATE UNIQUE NONCLUSTERED INDEX [IX_SF_CP_Detail_MAC2] ON [dbo].[SF_Cp_Detail] ( [Mac2] ASC ) WHERE ([MAC2]'' AND [MAC2] IS NOT NULL) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) GO 确认3.执行计划如下: 执行计划 SET STATISTICS IO ON ; (0 行受影响) Table 'SF_Cp_Detail'. Scan count 33, logical reads 1267942, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 场景二: 为什么用不到索引IX_SF_CP_Detail_MAC2]呢? 尝试1: 把唯一非聚集索引改为非聚集索引, Done,用到索引了. Drop index ……. CREATE NONCLUSTERED INDEX [IX_SF_Cp_Detail_Mac2] ON [dbo].[SF_Cp_Detail] ( [Mac2] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) GO 尝试2: 可是还需要验证唯一性怎么办呢? 试了半天未达到目标, 根据别人提示, 恢复唯一非聚集 CREATE UNIQUE NONCLUSTERED INDEX [IX_SF_CP_Detail_MAC2] ON [dbo].[SF_Cp_Detail] ( [Mac2] ASC ) WHERE ([MAC2]'' AND [MAC2] IS NOT NULL) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) GO 然后 修改查询: DECLARE @SN VARCHAR(12) SET @SN = '30F335CD0045' SELECT [Mac2] FROM SF_Cp_Detail WHERE [Mac2] = @SN option (recompile) 预计查询计划 看样子不行, 但是,看一下实际查询计划: OK ,Done,可以了. 尝试3. 但是我不可能去每个程序加option (recompile)呀, 而且随着数据量的增大,每次重新编译索引, 本身就导致SQL语句变慢. 最终解决方案: 唯一非聚集索引留着, 再添加一个 非聚集索引,保留两个索引, 终于搞定了. CREATE UNIQUE NONCLUSTERED INDEX [IX_SF_CP_Detail_MAC2] ON [dbo].[SF_Cp_Detail] ( [Mac2] ASC ) WHERE ([MAC2]'' AND [MAC2] IS NOT NULL) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING =

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值