索引的设计原则


索引有提高查询效率,但是给表的索引过多,效率反而会低下,因此在设计索引的时候,我们应该注意一些事项

  1. 区分度高得字段创建索引 。例如:学号身份证号
  2. 经常需要排序、分组和多表联合的字段创建索引
  3. 经常作为查询条件的字段创建索引
  4. 索引的数据不易过多
  5. 使用数据量少的索引(例如前缀索引,主要针对字符串索引)
  6. 对于多列索引,优先指定最左边的列集
  7. 删除不再使用或者很少使用的索引

索引的优化

单表查询优化

首先我们先看一下表的结构

索引设计以及优化

这里有两个索引,一个是主键索引,一个是name的索引。

我们查询SID = 1 的学生信息。

索引设计以及优化

首先MySQL默认使用INNODB的主键索引结构是B+树,非叶子节点存储主键关键字,叶子节点存储主键关键字和数据。
我们使用主键索引时,就在这个B+树上查询。索引SQL在执行过中,只需要查询一条数据(rows : 1)就能查询到了。

如果我们通过name = ‘赵雷’(赵雷的SID =1)去查询数据呢?

索引设计以及优化

我们可以看到,我们如果通过Sname 去查询,我们使用的是辅助索引 idx_name 此时,我们使用的不仅仅是辅助索引B+树了。(辅助索引B+树结构:非叶子节点保存关键字,叶子节点保存关键字和对应的主键)
我们在辅助索引上查询到对应的Sname = ‘赵雷’ 关键字后,我们得到对应的主键SID = 1,再去主键索引B+树上去查询对应的数据。

那么假如我们查询的是SID 呢(SID是主键)?

索引设计以及优化

那么我们就不需要再去主键索引上去寻找数据了,因为在辅助索引上已经查到了SID,已经满足查询了,不需要再去主键索引上去查询。

同样,我们如果查的是非主键字段
例如 Sage。

索引设计以及优化

这个过程就是通过Sname 辅助索引,在叶子节点上得到对应的SID =1 ,再去主键索引得到对应的所有数据,得到Sage。 

单表查询 + 简单排序或者分组

我们现在有一个订单表(orderlist)属性分别是用户ID,商品ID,时间。

索引设计以及优化

索引设计以及优化

我们查询userid =1 并按照日期排序。

索引设计以及优化

如果查询如果牵扯到排序也就是order by 语句。我们会发现它使用了 filesort
这是在对整个文件进行排序。效率是很低的。那么我们在使用排序查询的时候,我们就必须要避免filesort。我们一定要对使用filesort的查询进行优化

这时创建一个date的索引

索引设计以及优化

我们再来看看

索引设计以及优化

还是运用了filesort 。假如我们强制使用idx_date呢?
强制使用索引(在 where 之前加上 force index(索引名即可))
例如:select * from orderlist force index(idx_date) where userid = 1;

索引设计以及优化

这时我们强制使用了idx_date,不再filesort了。
我们使用了 idx_date 就无法使用其他索引了,假如我们既要排序,又要使用userid,那么我们该怎么做?
继续优化。
使用联合索引
那个属性在左边呢?经常使用的查询字段放在左边,因为创建了(idx_A,idx_B,idx_C),相当于创建了 (idx_A,idx_B) 和(idx_A)三个索引。当我们使用索引查询的时候,先比较第一个索引相不相等,相等则查询第二个索引。以此类推。
索引设计以及优化

这时不再使用 filesort 进行排序(为什么?因为索引的结构就是B+树,本身查询就是有序的,所以不需要排序)

额外知识:


假如有联合索引(idx_A,idx_B)
select * from 表 where A =1;
select * from 表 where B =1;
select * from 表 where A=1 and B =1;
那些查询会命中联合索引(这个表中的索引只有这一个索引)
我们就用orderlist来举例:
索引设计以及优化

表中只有一个userid和productid的联合索引

select * from 表 where A =1;命中

索引设计以及优化

select * from 表 where B =1;未命中

索引设计以及优化

select * from 表 where A=1 and B =1;命中

索引设计以及优化

所以综上所述,我们查询时创建的联合索引如果时(A,B)那么B字段单独查询时是不会命中索引的。

多表连接查询

这里我们以Student 和 SC表 为例

索引设计以及优化

student表中有一个主键索引 SID 以及 辅助索引dix_name
sc表中有 SID 有一个索引idx_id;

我们使用一次内连接查询
select * from Student a inner join SC b on a.SID = b.SID;
结果:

索引设计以及优化

 分析:

索引设计以及优化

发现a表(Student表)进行了全表扫面,b表(SC表)命中索引。
当我们进行多表联合查询数据时,MYSQL首先判断谁的表小(也就是谁的表数据少,行数少),小表总是进行全表扫面此时小表的索引是没有用的,但是大表会使用索引去查询,如果大表有对应的索引,就可以提高多表联合查询的查询效率。
也就说,小表决定了查询次数,大表决定查询时间

如果我们在使用联合查询的时候,使用了where条件过滤呢?

现在我们把子查询改为连接查询(内连接,左连接,外连接)
现在我们查询SID =1 的学生成绩和信息
结果:
select * from student a inner join SC b on a.SID = b.SID where b.SID =1;
索引设计以及优化

索引设计以及优化

 

我们发现现在a表(Student表)使用了索引,并且查询的数据变成了一行。
b表(SC表)使用了索引,并且查询的数据变成了18行。
我们加上了SID =1 这个条件,因为 Student 表存在SID的索引,直接使用了索引进行查询,使得 Student 表的数据从5行变成了1行,那么现在SC表是18行数据,Student表现在1行数据,谁大谁小?
很明显student表小,那么这时大表也就是SC的索引就能派上用场了,所以大表使用了索引进行查询

额外知识:子查询效率比连接查询要低
因为子查询会产生中间表,这样就使得效率低下。

索引设计以及优化

我们将语句改为连接查询。

索引设计以及优化

此时没有中间表产生。
(tips:为什么Student不使用索引进行查询?因为Student表的数据太少了,此时全表扫描比是使用索引实际上效率更高

索引失效:


当我们使用一些SQL查询会发现我们无法命中索引,此时我们称为SQL失效。

模糊查询like
通配符如果在前面那么索引就会失效例如“%xxx”
理论上我们使用Sname 查询会命中Sname 的索引,但是当使用模糊查询的通配符在前面时就会失效
索引设计以及优化

没有使用联合索引的第一列

 索引设计以及优化

 索引设计以及优化

索引失效,为了证明这一点我们再创建一个3个字段的联合索引。

索引设计以及优化

使用or 、 not in 、!= 、MYSQL函数 、类型转换都无法运用到索引。

我们讲一下没怎么接触过的类型转换。

索引设计以及优化

此时我们将int 类型强制转换为 varchar类型,索引无法使用索引。

索引设计以及优化 

 

类型正确时,我们还会运用到索引。

SQL和索引的优化


随着数据量的增长,有些效率低下的SQL语句会主键显现出来。此时我们应该对SQL和索引进行一系列的优化操作才可以。

慢查询日志


我们设置MySQL的慢查询日志,当SQL的执行时间超过我们预期,我们就把他加入到慢查询日志中去。
之后我们查询慢查询日志,去使用explain分析,为什么效率低下,分析,然后优化。
使用以下命令去设置预期时间。
show variables like ‘long%’;


索引设计以及优化

long_query_time 就是系统默认的慢查询时间 10s 。
我们可以对它进行设置
set long_query_time = xx;

索引设计以及优化 

慢查询日志的路径为:C:\Program Files (x86)\MySQL\MySQL Server 5.5\data
默认名为:host_name-slow.log

任务管理器查看I/O

任务管理器,性能,打开资源监视器,找到mysql,磁盘

 索引设计以及优化

索引设计以及优化 

这样我们就可以看到MySQL的读写状态了,当我们执行低效率SQL时,磁盘的交互是 M级以上,当我们正确使用索引时,交互就是K级别了。

优化总结


1.查询优化避免全表扫描,首先考虑再where 和 order by设计的列上建立索引。

2.注意 or 、 not in 、!= 、函数、类型转换会使索引失效。

2.1使用 union all 来代替or。
select id from t where num = 10 or num = 20;
改为:select id from t where num = 10 union all select id from t where num = 20;

2.2用 between and 代替 in
select id from t where num in(1,2,3)
改为 select id from t where num between 1 and 3;

3.避免在 where 后对字段进行操作,这会导致无法命中索引而进行全表扫描。
select id from t where num/2=100;
应该改为 select id from t where num = 100*2;

4.避免对字端进行函数操作
例如selecr id from t where substring(name,1,3) = ‘abc’
应该改为select id from t where name    like ‘abc%’;

5.不要再where 的 = 号左边进行函数,算数运算或者其他表达式的操作,否则无法正常使用索引。

6.使用索引字段作为条件时,如果是复合索引,必须使用索引的第一个字段作为查询条件。并尽可能的让查询条件的顺序和联合索引一致。

7.并不是所有索引对查询都有效,SQL是根据表中的数据进行查询优化的,当索引有大量数据重复时,SQL查询可能不会利用索引。例如表中有字段,sex,male,female,几乎各占一半,那么即使再sex上建立了索引,也对查询效率起不了什么作用。这是MYSQL的SQL优化器对索引的一种优化。

8.索引并不是越多越好,索引固然可以增加查询的效率,但同时也降低了insert 以及 update的效率,一个表的索引最好不要超过6个。

9.尽量使用数字字段来设置字段属性。

10.使用varchar/nvarchar 代替char/nchar。这样不仅会节省空间,并且一个较小的字段搜索效率也会高很多。
 

 

相关文章: