MySQL索引基本概念

索引

什么是索引

索引是存储引擎用于提高数据库表的访问速度的一种数据结构

索引的优缺点

优点:

  • 加快数据查找的速度
  • 为用来排序或者是分组的字段添加索引,可以加快分组和排序的速度
  • 加快表与表之间的连接

缺点:

  • 建立索引需要占用物理空间
  • 会降低表的增删改的效率,因为每次对表记录进行增删改,需要进行动态维护索引,导致增删改时间变长

索引的作用

数据是存储在磁盘上的,查询数据时,如果没有索引,会加载所有的数据到内存,依次进行检索,读取磁盘次数较多。有了索引,就不需要加载所有数据,因为B+树的高度一般在2-4层,最多只需要读取2-4次磁盘,查询速度大大提升

什么情况下需要建索引

  1. 经常用于查询的字段
  2. 经常用于连接的字段建立索引,可以加快连接的速度
  3. 经常需要排序的字段建立索引,因为索引已经排好序,可以加快排序查询速度

什么情况下需要建索引

  1. where条件中用不到的字段不适合建立索引
  2. 表记录较少。比如只有几百条数据,没必要加索引。
  3. 需要经常增删改。需要评估是否适合加索引
  4. 参与列计算的列不适合建索引
  5. 区分度不高的字段不适合建立索引,如性别,只有男/女/未知三个值。加了索引,查询效率也不会提高。

索引的数据结构

索引的数据结构主要有B+树和哈希表,对应的索引分别为B+树索引和哈希索引。InnoDB引擎的索引类型有B+树索引和哈希索引,默认的索引类型为B+树索引。

B+树索引

B+ 树是基于B 树和叶子节点顺序访问指针进行实现,它具有B树的平衡性,并且通过顺序访问指针来提高区间查询的性能。

在 B+ 树中,节点中的 key 从左到右递增排列,如果某个指针的左右相邻 key 分别是 keyi 和 keyi+1,则该指针指向节点的所有 大于等于 keyi 且小于等于 keyi+1

B+ 树索引的本质就是 B+ 树在数据库中的实现,它是目前关系型数据库系统中查找最为常用的索引。

B+ 树是从最早的平衡二叉树演化而来的,但是 B+ 树不是一个二叉树。

简单介绍下:B+ 树是为磁盘或其他直接存取辅助设备设计的一种平衡查找树。在 B+ 树中,所有记录节点都是按键值的大小顺序存放在同一层的叶子节点上,各叶子节点之间通过双向链表进行连接。

也就是说,B+ 树的叶子节点存储真正的记录,而非叶子节点的存在是为了更快速的找到对应记录所在的叶子节点。如下图是一个高度为 2 的 B+ 树:

进行查找操作时,首先在根节点进行二分查找,找到key所在的指针,然后递归地在指针所指向的节点进行查找。直到查找到叶子节点,然后在叶子节点上进行二分查找,找出所对应key的数据项。
MySQL索引基本概念_第1张图片

哈希索引

哈希索引是基于哈希表实现的,对于每一行数据,存储引擎会对索引列进行哈希计算得到哈希码,并且哈希算法要尽量保证不同的列值计算出的哈希码值是不同的,将哈希码的值作为哈希表的key值,将指向数据行的指针作为哈希表的value值。这样查找一个数据的时间复杂度就是O(1),一般多用于精确查找。但不适合范围查找

所谓哈希索引也就是得益于哈希算法的快速查找特性,不过哈希索引的致命缺点就是无法范围查询。并且 InnoDB 中哈希索引是自适应的,也就是说 InnoDB 存储引擎会根据表的使用情况自动为表生成哈希索引,不能人为干预是否在一张表中生成哈希索引

hash索引和b-树索引的区别
  • 哈希索引不支持排序,因为哈希表是无序的。
  • 哈希索引不支持范围查找
  • 哈希索引不支持模糊查询及多列索引的最左前缀匹配。
  • 因为哈希表中会存在哈希冲突,所以哈希索引的性能是不稳定的,而B+树索引的性能是相对稳定的,每次查询都是从根节点到叶子节点。
为什么b-树比b树更适合实现数据库索引
  • 由于B+树的数据都存储在叶子结点中,叶子结点均为索引,方便扫库,只需要扫一遍叶子结点即可,但是B树因为其分支结点同样存储着数据,我们要找到具体的数据,需要进行一次中序遍历按序来扫,所以B+树更加适合在区间查询的情况,而在数据库中基于范围的查询是非常频繁的,所以通常B+树用于数据库索引。
  • B+树的节点只存储索引key值,具体信息的地址存在于叶子节点的地址中。这就使以页为单位的索引中可以存放更多的节点。减少更多的I/O支出。
  • B+树的查询效率更加稳定,任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。

索引有什么分类?

1 主键索引

名为primary的唯一非空索引,不允许有空值。

2 唯一索引

索引列中的值必须是唯一的,但是允许为空值
唯一索引和主键索引的区别是:唯一索引字段可以为null且可以存在多个null值,而主键索引字段不可以为null
唯一索引的用途:唯一标识数据库表中的每条记录,主要是用来防止数据重复插入。

3 组合索引

在表中的多个字段组合上创建的索引,只有在查询条件中使用了这些字段的左边字段时,索引才会被使用,使用组合索引时需遵循最左前缀原则。

4 全文索引

只能在CHAR VARCHARTEXT类型字段上使用全文索引。

5 普通索引

普通索引是最基本的索引,它没有任何限制,值可以为空。

什么是最左匹配原则

如果 SQL 语句中用到了组合索引中的最左边的索引,那么这条 SQL 语句就可以利用这个组合索引去进行匹配。当遇到范围查询(><betweenlike)就会停止匹配,后面的字段不会用到索引。

(a,b,c) 建立索引,查询条件使用 a/ab/abc 会走索引,使用 bc 不会走索引。

(a,b,c,d)建立索引,查询条件为 a = 1 and b = 2 and c > 3 and d = 4,那么a、b和c三个字段能用到索引,而d无法使用索引。因为遇到了范围查询。

如下图,对(a, b) 建立索引,a 在索引树中是全局有序的,而 b 是全局无序,局部有序(当a相等时,会根据b进行排序)。直接执行b = 2这种查询条件无法使用索引。

MySQL索引基本概念_第2张图片

当a的值确定的时候,b是有序的。例如a = 1时,b值为1,2是有序的状态。当a = 2时候,b的值为1,4也是有序状态。 当执行a = 1 and b = 2时,a和b字段能用到索引。而执行a > 1 and b = 2时,a字段能用到索引,b字段用不到索引。因为a的值此时是一个范围,不是固定的,在这个范围内b值不是有序的,因此b字段无法使用索引。

什么是聚集索引?

聚集索引 clustered index

聚集索引的叶子节点就是整张表的行记录。InnoDB 主键使用的是聚簇索引。聚集索引要比非聚集索引查询效率高很多。

先来看聚集索引,对于 InnoDB 存储引擎来说,表采用的存储方式称为索引组织表(index organizedtable),也即表都是根据主键的顺序来进行组织存放的

聚集索引就是按照每张表的主键构造一棵 B+ 树,同时叶子节点中存放的即为表中一行一行的数据,所以聚集索引的叶子节点也被称为数据节点。
MySQL索引基本概念_第3张图片

也就是说,聚集索引能够在 B+ 树索引的叶子节点上直接找到数据。并且由于定义了数据的逻辑顺序,查询优化器能够快速发现到底是哪一段范围的数据页需要扫描。比如用户需要查询一张用户表,查询最后注册的 10 位用户,由于 B+ 树索引的叶子节点是基于双向链表的,所以用户可以快速找到最后一个数据页,并取出 10 条记录。这也就是为什么大部分情况下查询优化器倾向于采用聚集索引了。

可以这么说:在聚集索引中,索引即数据,数据即索引

另外,由于数据页只能按照一棵 B+ 树进行查找排序,或者说无法同时把数据行存放在两个不同的地方,所以每张表只能拥有一个聚集索引

如果主键都没有,表怎么存???

主键和聚集索引的关系

不显示定义主键 != 没有主键。

如果在创建表时没有显式地定义主键,InnoDB 存储引擎会按如下方式选择或创建主键:

  • 首先判断表中是否有非空唯一索引(Unique NOT NULL),如果有,则该列即为主键
  • 如果不符合上述条件,InnoDB 存储引擎自动创建一个 6 字节大小的指针 _rowid 作为主键

如果表中有多个非空唯一索引时怎么办呢? InnoDB 存储引擎将选择建表时第一个定义的非空唯一索引为主键。需要注意的是!主键的选择根据的是非空唯一索引定义的顺序,而不是建表时列的顺序。

主键和索引就不是一个层次的东西!

主键是一种约束,这个约束用来强制表的实体完整性,一个表中只能有一个主键约束,并且主键约束中的列值必须是非空且唯一的。

而聚集索引它作为一种索引,其目的不是为了约束啥,而是为了对数据行进行排序以提高查询的效率,换句话说它决定的是数据库的物理存储结构。

⭐ 形象点说,一个没加聚集索引的表,它的数据是一行一行 无序 地存放在磁盘存储器上的。而如果给表添加了聚集索引,那么表在磁盘上的存储结构就由一行一行排列的结构转变成了 树状结构,也就是 B+ 树结构,换句话说,就是整个表就变成了一个索引,也就是上面提到的 “索引即数据,数据即索引”。

所以,不要说 “主键就是聚集索引”,应该这样说:“聚集索引一般都是加在主键上的”。

辅助索引 **Secondary index **

也称为 非聚集索引、二级索引

区别: 辅助索引的叶子节点并不包含行记录的全部数据。

简单来说,一行记录我们可以用 “key + value” 这样的组合来标识,聚集索引中的叶子节点存储的就是这一整个组合,而非聚集索引中的叶子节点只存储了这个组合中的 key,

也就是说,辅助索引的叶子节点包含的是:每行数据的辅助索引键 + 该行数据对应的聚集索引键

假设有张 user 表,包含 id(主键),name,age(普通索引)三列,有如下数据:

id	name	    age
1	Jack	    18
7	Alice	    28
10	Bob	    	38
20	Carry	    48

画一个比较简单比较容易懂的图来看下聚集索引和辅助索引:

  • 聚集索引(id):
    MySQL索引基本概念_第4张图片

  • 辅助索引(age):

MySQL索引基本概念_第5张图片

什么是覆盖索引?

覆盖索引的目的就是避免发生回表查询,也就是说,通过覆盖索引,只需要扫描一次 B+ 树即可获得所需的行记录

select的数据列只用从索引中就能够取得,不需要回表进行二次查询,也就是说查询列要被所使用的索引覆盖。对于innodb表的非聚集索引(辅助索引),如果该索引能覆盖到查询的列,那么就可以避免对主键索引(聚集索引)的二次查询。

什么是回表查询

在上述的表结构中, 如果查询条件为主键,则只需扫描一次聚集索引的 B+ 树即可定位到要查找的行记录。举个例子:

select * from user where id = 7;

查找过程如上聚集索引(id)图中的绿色所示:
MySQL索引基本概念_第6张图片

如果查询条件为普通索引(辅助索引) age,则需要先查一遍辅助索引 B+ 树,根据辅助索引键得到对应的聚集索引键,然后再去聚集索引 B+ 树中查找到对应的行记录。举个例子:

select * from user where age = 28;

上述 select * 等同于 select id, age, name 对吧,id 是主键索引,age 是普通索引,而 name 并不存在于 age 索引的 B+ 树上,所以通过 age 索引查询到 id 和 age 的值之后,还需要去聚集索引上才能查到 name 的值。

如图所示,第一步,查 age 辅助索引:
MySQL索引基本概念_第7张图片

第二步,查聚集索引:

MySQL索引基本概念_第8张图片

这就是所谓的回表查询,因为需要扫描两次索引 B+ 树,所以很显然它的性能较扫一遍索引树更低。

覆盖索引的目的就是避免发生回表查询,也就是说,通过覆盖索引,只需要扫描一次 B+ 树即可获得所需的行记录。

如何实现覆盖索引

上文解释过,下面这个 SQL 语句需要查询两次 B+ 树:

select * from user where age = 28

我们将其稍作修改,使其只需要查询一次 B+ 树:

select id from user where age = 28;

之前我们的返回结果是整个行记录,现在我们的返回结果只需要 id

id 是什么?主键索引(聚集索引),age 是什么?普通索引(辅助索引),age 索引的 B+ 树的叶子节点存储的是什么?辅助索引键 + 对应的聚集索引键

所以这条 SQL 语句只需要扫描一次 age 索引的 B+ 树就可以直接提供查询结果,不需要回表。也就是说,在这个查询里面,索引 age 已经 覆盖了 我们的查询需求,我们称为覆盖索引(所以覆盖索引其实就是一种联合索引)。

由于覆盖索引可以减少树的搜索次数,显著提升查询性能,所以使用覆盖索引是一个常用的性能优化手段。

再回到这条语句:

select * from user where age = 28;

如果想要使用覆盖索引对这条语句进行优化,该如何做?

很简单了,对吧,我们只需要把 age,name 设置为联合索引就可以了:

create index idx_age_name on user(`age`,`name`);

此时 age 和 name 作为辅助索引键都在同一棵辅助索引的 B+ 树上,所以只需扫描一次这个组合索引的 B+ 树即可获取到 id、age 和 name,这就是实现了索引覆盖。

你可能感兴趣的:(mysql,mysql,数据结构)