数据库索引《二》

目录

覆盖索引

最左前缀原则

索引下推

思考及解答


设计表结构或者是创建索引的时候,我们的目标就是在满足当前需求的情况下,减少对数据库的访问,减少资源的消耗。

//表结构
USER_TABLE | CREATE TABLE `USER_TABLE` (
  `id` int(11) NOT NULL,
  `age` int(11) NOT NULL,
  `orderId` bigint DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `orderId` (`orderId`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 

//插入数据
insert into students (id, age, orderId) values (1,18,101),(2,19,102),(3,20,103),(4,21,104),(5,22,105);

//查询sql
select * from USER_TABLE where orderId between 102 and 103;

下面来看一下上面的查询sql语句执行的流程

1、在普通orderId索引上找到orderId=102的记录,获取id的值2;
2、再到主键索引树上查询 id=2 对应的行记录;
3、在普通orderId索引上找到orderId=103的记录,获取id的值3;
4、再到主键索引树上查询 id=3 对应的行记录;
5、再到普通orderId索引上找到orderId=104的记录,不满足条件,循环结束;
    通过第一节,我们知道完成的行记录信息是存储在主键索引上的,普通索引只是存储了索引值和主键key的数据。1-3-5步骤是查询了普通索引树上的值,然后回表了2次主键索引才获取到结果。那么我们只要避免回表就可以提高查询的速度

覆盖索引

   这里就用到了覆盖索引,如果执行的语句是:


select id from USER_TABLE where orderId between 102 and 103;

    这时候只需要查询id的值,而id的值已经在普通索引orderId上了,可以直接查询结果,而不需要回表。也就是说,普通索引orderId已经“覆盖了”我们的查询要求,所以我们成为覆盖索引。
    由于覆盖索引可以减少树的搜索次数,避免了回表查询,显著提升查询性能,所以使用覆盖索引是一个常用的性能优化手段。
需要注意的是:覆盖索引在引擎内部使用覆盖索引在索引orderId上其实读了3个记录,但是对于server层来说,它只拿到了2条记录,所以认为扫描数是2条。   

   当我们需要建立冗余索引来支持覆盖索引时就需要权衡考虑其维护代价和查询的效率了。
   例如我们一般很少对姓名“name”字段来做索引,基本上都是对身份证id或者userId来建立索引,但是如果我们有通过userId查询“name”的需求的时候,数据量还比较大,又不想对"name"单独的建立索引,查询需求少且增加了维护成本,这时候使用联合索引(userId,name)就有意义了。在这个请求上不需要回表查整个记录,减少语句的执行时间。

最左前缀原则

      当某个查询频率不高,但是存在的请求,考虑到空间及维护成本,我们不希望为它单独建立一个索引,但是又不希望它扫描全表,这时,我们就会想到最前缀索引,在B+树中,我们可以利用“最左前缀”,来定位记录。
      最左前缀索引就是利用联合索引的最左N个字段,也可以是字符串索引的最左个字符;
    这里我们需要考虑一个问题:在建立联合索引的时候,如何安排索引内的字段顺序

  • 原则一:如果通过调整可以少维护一个索引,那么这个顺序就是需要优先考虑的;
  • 原则二:空间内存的占用;

   比如,对于身份证id和userId,我们既需要联合索引(id,userId),又需要id和userId各自的索引,这时候就要考虑,看那个字段更大,如果userId比id大,我们就需要建立(userid,id)和id的索引,这样既可以满足查询需求,又可以节省空间。

索引下推

     前面提到最左前缀索引,我们可以通过最左前缀来定位索引,那么那些不符合最左前缀的部分呢?
     比如有一联合索引是(name,age)。现在需要查出姓李的年龄是12岁的男同学。

select * from USER_TABLE where name like “李%” and age = 12 and is male = 1;

    这条语句在使用的时候只能用到”李"的索引,找到第一个满足条件姓李的同学,然后再匹配剩下的条件。

     在mysql5.6之前,只能从找到的id将每条数据一个个开始回表,比对字段值查询,然后返回;
     但是mysql5.6以后引入索引下推优化【index condition pushdown 】简称icp,意思就是在索引遍历的结果中,先通过右侧索引包含的字段直接过滤掉不满足的条件记录,减少回表的次数这里把条件判断提前了,而不是访问完磁盘查询到结果放到结果集前才去判断过滤。比如在索引下推的情况下,在查询的结果中,会过滤掉姓李,年龄不是12的女同学,这部分数据是不需要回表查询的,减少了回表的次数,提高了访问效率。这个优化我觉得还是比较合理的,不然多个联合索引将造成右侧索引的浪费,没有物尽其用。

思考及解答

问题一:sql语句的执行流程,回表/覆盖索引/联合索引/最左前缀/索引下推的概念及优缺点?

答:问中已经提到,这里不字啊赘述。

问题二:主键索引一定比二级索引快吗?
答:不一定,覆盖索引不需要回表了,数据量大时,二级索引可能比主键索引快;

问题三:联合索引的使用技巧有那些?
1、覆盖索引:如果查询条件是普通索引或者是联合索引的最左侧字段,查询结果是联合索引的字段或者是主键,此时不需要回表,直接返回结果,减少io磁盘的访问,提高效率;
2、最左前缀:联合索引的最左N个字段,也可以是字符串索引的最左M个字符;
3、联合索引:联合索引的使用遵循最左前缀原则,所以尽量将频繁查询的字段靠左创建;
4、索引下推:例如”select * from USER_TABLE where name like “李%” and age = 12”,mysql5.6版本之前,会对所有匹配的数据进行回表操作,但是5.6之后的版本,会先过滤age!=12的数据,再进行回表,减少了访问磁盘的次数,提升检索效率;
Ps:使用联合索引时,sql语句中的字段不必和创建的联合索引顺序一致,因为优化器回帮你优化顺序;

问题四:索引的创建原则
答:高频查询:可以建立联合索引来使用覆盖索引,不用回表;
      非高频查询:在已有的联合索引基础上,使用最左前缀原则来快速查询;
      mysql5.6: 引入索引下推,减少回表次数;

问题五:联合索引在索引下推优化的情况下的执行过程?
联合索引的使用方法:使用最左字节做快速定位,然后由于有index condition pushdown 优化,接下来遍历索引过程中就可以用右边的字段来过滤掉不需要的记录,得到结果返回给server层,判断是否继续;

select  * from USER_TABLE where age > 10 and age < 12 and name =“张三”;

在icp作用下的执行流程:

  • (1) server层将age>10 and name =“张三” 传入引擎:
  • (2) 引擎「快速定位」找到第一个 age > 10的行,如果发现name<>”zhangsan”,则找下一个,直到满足name=“张三”;
  • (3) 把找到的行返回给server层,server层根据age是否大于12来决定要不要取下一个;

总之:联合索引因为有了icp优化,所以还是应该尽量将查询的字段放入联合索引中。

问题六:优化的方向:

  • 1. 减少数据访问(减少磁盘访问) :使用索引;
  • 2. 返回更少数据(减少网络传输或磁盘访问) :数据分页处理、只返回需要的字段;【limit 1和limit 10000000,5 语句的优化】
  • 3. 减少交互次数(减少网络传输) :批量执行、使用存储过程;
  • 4. 减少服务器CPU开销(减少CPU及内存开销):使用绑定变量、合理使用排序 【order by的两种排序,内存的占用,避免排序】、减少比较操作、大量复杂运算在客户端处理;

索引优化小技巧:

  1. 数据区分度不大,不建议使用索引:where gender = 'M';性别只有男、女、未知三种;
  2. 尽量不好使用负向查询,例如:!=、not in、not exists;
  3. 单列索引不存null值,复合索引不存全为null的值(索引列尽量使用not null的约束并设置默认值);
  4. limit 的使用;
  5. order by 的使用;
  6. 避免select * ;分页机返回必要的字段即可;
  7. 尽量使用count(*),不要使用count(字段)/(id)/(1) ;

学习笔记,内容简单,用于复习,原内容2月有更新。
##参考资料,《MySql实战详解》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章