zoukankan      html  css  js  c++  java
  • 什么是聚集索引、非聚集索引、覆盖索引?

    什么是覆盖索引?

    本文为笔者近来学习的笔记,在解释覆盖索引之前势必简单回顾一下索引基本知识?

    索引

    索引是数据库管理系统中一个排序的数据结构,以协助快速查询、更新数据库表中数据。通常类比为图书目录。

    聚集索引与非聚集索引

    聚集索引:

    聚集索引中键值的逻辑顺序决定了表中相应行的物理顺序,例如电话本,索引为(姓,名),数据值为电话号,在一个表中通常只有一个聚集索引, 聚集索引对于那些经常要搜索范围值的列特别有效。使用聚集索引找到包含第一个值的行后,便可以确保包含后续索引值的行在物理相邻。

    ​ 例如,如果应用程序执行 的一个查询经常检索某一日期范围内的记录,则使用聚集索引可以迅速找到包含开始日期的行,然后检索表中所有相邻的行,直到到达结束日期。这样有助于提高此类查询的性能。同样,如果对从表中检索的数据进行排序时经常要用到某一列,则可以将该表在该列上聚集(物理排序),避免每次查询该列时都进行排序,从而节 省成本。
      当索引值唯一时,使用聚集索引查找特定的行也很有效率。

    非聚集索引

    一种索引,该索引中索引的逻辑顺序与磁盘上行的物理存储顺序不同。我们可以这么理解聚簇索引:索引的叶节点就是数据节点。而非聚簇索引的叶节点仍然是索引节点(例如存储数据存放的地址,而非数据),有一个指针指向对应的数据块。

    ​ 我们的汉语字典的正文本身就是一个聚集索引。比如,我们要查“安”字,就会很自然地翻开字典的前几页,因为“安”的拼音是“an”,而按照拼音排序汉字的字典是以英文字母“a”开头并以“z”结尾的,那么“安”字就自然地排在字典的前部。如果您翻完了所有以“a”开头的部分仍然找不到这个字,那么就说明您的字典中没有这个字;同样的,如果查“张”字,那您也会将您的字典翻到最后部分,因为“张”的拼音是“zhang”。也就是说,字典的正文部分本身就是一个目录,您不需要再去查其他目录来找到您需要找的内容。我们把这种正文内容本身就是一种按照一定规则排列的目录称为“聚集索引”。

    ​ 如果您认识某个字,您可以快速地从自动中查到这个字。但您也可能会遇到您不认识的字,不知道它的发音,这时候,您就不能按照刚才的方法找到您要查的字,而需要去根据“偏旁部首”查到您要找的字,然后根据这个字后的页码直接翻到某页来找到您要找的字。但您结合“部首目录”和“检字表”而查到的字的排序并不是真正的正文的排序方法,比如您查“张”字,我们可以看到在查部首之后的检字表中“张”的页码是672页,检字表中“张”的上面是“驰”字,但页码却是63页,“张”的下面是“弩”字,页面是390页。很显然,这些字并不是真正的分别位于“张”字的上下方,现在您看到的连续的“驰、张、弩”三字实际上就是他们在非聚集索引中的排序,是字典正文中的字在非聚集索引中的映射。我们可以通过这种方式来找到您所需要的字,但它需要两个过程,先找到目录中的结果,然后再翻到您所需要的页码。我们把这种目录纯粹是目录,正文纯粹是正文的排序方式称为“非聚集索引”。

    索引分类

    主键索引(PRIMARY),以主键列作为索引,最常用的索引。

    普通索引(INDEX):以普通列作为索引,唯一任务是加快对数据的访问速度,因此,应该只为那些最经常出现在查询 条件(WHERE column=)或者排序条件(ORDERBY column)中的数据列创建索引。

    唯一索引(UNIQUE):以唯一值作为索引,目的往往不是为了提高访问速度,而只是为了避免数据出现重复。

    组合索引(Composite):多列组合起来作为索引,例如前文中提到的电话簿,就是以姓+名的形式作为索引

    覆盖索引前因:回表

    ​ InnoDB引用的是B+树索引模型,前文中对索引的种类划分为两大类:主键(聚集)索引和非聚集索引,那么问题就在于比较两种索引的区别了,我们这里建立一张学生表,其中包含字段id设置主键索引、name设置普通索引、age(无处理),并向数据库中插入4条数据:("小赵", 10)("小王", 11)("小李", 12)("小陈", 13)。

    CREATE TABLE `student` (
      `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增主键',
      `name` varchar(32) COLLATE utf8_bin NOT NULL COMMENT '名称',
      `age` int(3) unsigned NOT NULL DEFAULT '1' COMMENT '年龄',
      PRIMARY KEY (`id`),
      KEY `I_name` (`name`)
    ) ENGINE=InnoDB;
    
    INSERT INTO student (name, age) VALUES("小赵", 10),("小王", 11),("小李", 12),("小陈", 13);
    
    

    此时表中的数据:

    mysql> select * from student;
    +----+------+-----+
    | id | name | age |
    +----+------+-----+
    |  1 | 小赵 |  10 |
    |  2 | 小王 |  11 |
    |  3 | 小李 |  12 |
    |  4 | 小陈 |  13 |
    +----+------+-----+
    4 rows in set (0.00 sec)
    

    每一个索引在 InnoDB 里面对应一棵B+树,那么此时就存着两棵B+树:

    可以发现区别在与叶子节点中,主键索引存储了整行数据,而非主键索引中存储的值为主键id

    当我们执行以下语句:

    select age from student where name = '小李';
    

    执行顺序:

    1. 在name索引树上找到名称为小李的节点 id为03
    2. 从id索引树上找到id为03的节点 获取所有数据
    3. 从数据中获取字段命为age的值12,返回.

    这样从非主键索引树搜索再回到主键索引树搜索的过程称为:回表

    回表一定程度上消耗性能,那么如何降低这种性能损耗呢?于是提出了一种方法:覆盖索引.

    覆盖索引

    覆盖索引(covering index ,或称为索引覆盖)即从非主键索引中就能查到的记录,而不需要查询主键索引中的记录,避免回表的产生减少了树的搜索次数,显著提升性能。

    覆盖索引的使用

    如果一个业务中,很多类似于根据姓名查找年龄的业务,那么可以将这些热点业务重新根据(name , age)建立联合索引,先删除之前以name构建的索引:

    ALTER TABLE student DROP INDEX I_name;
    ALTER TABLE student ADD INDEX I_name_age(name, age);
    

    联合索引:

    再次执行如下sql:

    select age from student where name = '小李';
    

    执行流程:

    1. 在name,age联合索引树上找到名称为小李的节点。
    2. 此时节点索引里包含信息age直接返回 12,从而避免回表。

    如何确定使用的是覆盖索引还是主键索引呢?

    当发起一个索引覆盖查询时,在explain的extra列可以看到using index的信息:

  • 相关阅读:
    对象进行比较
    java中length,length(),size()区别
    引用数据类型、自定义类
    java方法
    学员信息管理系统案例
    商品库存管理查看增减
    引用数据类型Scanner,Random
    Cantor表巧妙的做法
    UVA 11292 The dragon of Loowater勇士斗恶龙 11729 突击战 Commando War
    期末,祝不挂
  • 原文地址:https://www.cnblogs.com/Courage129/p/14166775.html
Copyright © 2011-2022 走看看