MySQL索引概述及索引的分类

概述

MySQL官方对索引的定义为:索引(index)是帮助MySQL高效获取数据的数据结构(有序)。在数据之外,数据库系统还维护者满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据, 这样就可以在这些数据结构上实现高级查找算法,这种数据结构就是索引。

MySQL索引概述及索引的分类

左边是数据表,一共有两列七条记录,最左边的是数据记录的物理地址(注意逻辑上相邻的记录在磁盘上也并不是一定物理相邻的)。为了加快Col2的查找,可以维护一个右边所示的二叉查找树,每个节点分别包含索引键值和一个指向对应数据记录物理地址的指针,这样就可以运用二叉查找快速获取到相应数据。(在有聚簇索引的概念时指向聚簇索引的值)

一般来说索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储在磁盘上。索引是数据库中用来提高性能的最常用的工具。

索引是在MySQL的存储引擎层中实现的,而不是在服务器层实现的。所以每种存储引擎的索引都不一定完全相同,也不是所有的存储引擎都支持所有的索引类型的。MySQL目前提供了以下4种索引:

  • BTREE 索引 : 最常见的索引类型,大部分索引都支持 B 树索引。
  • HASH 索引:只有Memory引擎支持 , 使用场景简单 。
  • R-tree 索引(空间索引):空间索引是MyISAM引擎的一个特殊索引类型,主要用于地理空间数据类型,通常使用较少,不做特别介绍。
  • Full-text (全文索引) :全文索引也是MyISAM的一个特殊索引类型,主要用于全文索引,InnoDB从Mysql5.6版本开始支持全文索引。

MyISAM、InnoDB、Memory三种存储引擎对各种索引类型的支持

索引 InnoDB引擎 MyISAM引擎 Memory引擎
BTREE索引 支持 支持 支持
HASH 索引 不支持 不支持 支持
R-tree 索引 不支持 支持 不支持
Full-text 5.6版本之后支持 支持 不支持

索引分类——从逻辑角度

  1. 主键索引
    索引列的值必须唯一,并且不允许有空值
  2. 唯一索引
    索引列的值必须唯一,但允许有空值
  3. 单值索引
    一个索引只包含单个列,一个表可以有多个单列索引
  4. 复合索引
    一个索引包含多个列

尽量使用复合索引,而少使用单列索引 。

创建复合索引

create index idx_name_sta_address on tb_seller(name, status, address);

就相当于创建了三个索引 :
+ name
+ name + status
+ name + status + address

索引分类——从数据结构角度

B树,B+树

之前的博客有介绍多叉平衡查找树-B树与B-树

通常在 B+树上有两个头指针,一个指向根结点(进行随机搜索),一个指向关键字最小的叶结点(进行顺序搜索)。

B+树与B树的比较

组织方式不一样

  • B+树:所有有效的索引关键字值都必须存储在叶结点中,其内部结点中的键值只用于索引项的查找定位。
  • B树:有效的索引关键字值可以出现在B树的任意一个结点中。

因此:
+ B+树:所有关键字的查找速度基本一致
+ B树:依赖于查找关键字所在结点的层次

叶结点不同

B+树中叶节点间增加链表指针,提供对索引关键字的顺序扫描功能;叶节点的个数未必符
合 m 叉查找树的要求,它依赖于键值字节数和指针字节数,为 m1 阶。

MySQL中的B+树适用场景

InnoDB 存储引擎使用的是 B+树。

B+树为对如下类型的查询有效:

  1. 全值匹配:和索引中的所有列进行匹配(复合索引)
  2. 匹配最左前缀:只使用索引的第一列或前几列
  3. 匹配列前缀:只匹配某一列的值的开头部分
  4. 匹配范围值
  5. 精确匹配某一列并范围匹配另外一列
  6. 覆盖索引/只访问索引的查询

一般来说,如果 B+树可以按照某种方式查找到值,那么也可以按照这种方式用于排序。如果 ORDER BY 子句满足前面列出的几种查询类型,则这个索引也可以满足对应的排序需求。

下面是一些关于B+树索引的限制:

  1. 如果不是按照索引的最左列开始查找,则无法使用索引
  2. 不能跳过索引中的列
  3. 如果查询中有某个列的范围查询,则其右边所有列都无法使用索引优化查找

Hash 索引

Hash树我在博客高效查找的数据结构-HashTree(哈希树)也提到过了。

只有精确匹配索引所有列的查询才有效,在 MySQL 中,只有 Memory 引擎显式支持 Hash 索引。

限制:
1. 哈希索引只包含哈希值和行指针,而不存储字段值,所以不能使用索引中的值来避免读取行(无法使用覆盖索引)。不过,访问内存中的行的速度很快。
2. 哈希索引数据并不是按照索引值顺序存储的,所以无法进行排序
3. 哈希索引不支持部分索引列匹配查找。比如建立复合哈希索引(A,B),无法仅使用 A 使用哈希索引去查询
4. 不支持范围查询,仅支持等值查询
5. 哈希冲突严重时,索引维护的代码很高。

B树索引与Hash索引比较

  1. 如果是等值查询,那么哈希索引明显有绝对优势,因为只需要经过一次算法即可找到相应的键值;当然了,这个前提是,键值都是唯一的。如果键值不是唯一的,就需要先找到该键所在位置,然后再根据链表往后扫描,直到找到相应的数据;如果是范围查询检索,这时候哈希索引就毫无用武之地了,因为原先是有序的键值,经过哈希算法后,有可能变成不连续的了,就没办法再利用索引完成范围查询检索;
  2. 哈希索引也没办法利用索引完成排序,以及 like ‘xxx%’ 这样的部分模糊查询(这种部分模糊查询,其实本质上也是范围查询);
  3. 哈希索引也不支持多列联合索引的最左匹配规则;
  4. B+树索引的关键字检索效率比较平均,在有大量重复键值情况下,哈希索引的效率是极低的,因为存在所谓的哈希碰撞问题。

索引分类——从物理存储角度

聚簇索引(聚集索引)

InnoDB 的聚簇索引实际上在同一个结构中保存了B+树索引和数据行。

当表有聚簇索引时,它的数据行实际上存放在索引的叶子页中。聚簇表示数据行和相邻的键值紧紧地存储在一起。因为无法同时把数据行存储在两个不同的地方,所以一个表只能有一个聚簇索引。

InnoDB 通过主键聚簇数据。

每张表都会有一个聚簇索引。聚簇索引是一级索引。

聚簇索引一般是主键;没有主键,就是第一个唯一键;没有唯一键,就是隐藏ID。聚簇索引以外的所有索引都称为二级索引(即非聚簇索引)。 在 InnoDB 中,二级索引中的每条记录都包含该行的主键列,以及为二级索引指定的列。 InnoDB 使用这个主键值来搜索聚簇索引中的行。

聚簇索引的优点:
1. 可以将相关数据保存在一起,只需一次 IO 就可以取出相邻的数据
2. 数据访问更快,因为索引和数据保存在同一个 B+树中
3. 使用覆盖索引扫描的查询可以直接使用叶节点中的主键值

缺点:
1. 插入速度严重依赖于插入顺序。按照主键的顺序插入是加载数据到 InnoDB 表中速度最快的方式。但如果不是按照主键顺序加载数据,那么在加载完成后最好使用 OPTIMIZE TABLE命令重新组织一下表
2. 更新聚簇索引列的代价很高,因为会强制 InnoDB 将每个被更新的行移动到新的位置
3. 插入新行或者更新主键导致需要移动行的时候,可能面临页分裂的问题。当行的主键值要求必须将这一行插入到某个已满的页中时,存储引擎会将该页分裂成两个页面来容纳该行,这就是一次页分裂操作。页分裂会导致表占用更多的磁盘空间。
4. 可能导致全表扫描变慢,尤其是行比较稀疏,或者由于页分裂导致数据存储不连续的时候
5. 二级索引(非聚簇索引)可能会更大, 因为在二级索引的叶子节点包含了引用行的主键值。这样的策略减少了当出现行移动或者页分裂时二级索引的维护工作。
6. 二级索引访问需要两次 B 树索引查找,而不是一次。因为二级索引中叶子节点保存的是行的主键值,要找到数据行,还需要拿主键值到聚簇索引中进行一次查找。

对于 InnoDB,自适应哈希索引能够减少这样的重复工作。

非聚簇索引(辅助索引)

就是不是聚簇索引,只存储了聚簇索引的值,若根据非聚簇索引查找非覆盖索引项则需要回表(拿聚簇索引值)再查找一遍。

索引使用的基本原则

  • 最经常查询的列上建立聚簇索引以提高查询效率
  • 一个基本表最多只建立一个聚簇索引
  • 经常更新的列不宜建立聚簇索引
  • 主键和唯一键会自动创建索引

SQL索引语法

创建索引

CREATE [UNIQUE|FULLTEXT|SPATIAL] INDEX index_name 
[USING index_type]
ON tbl_name(index_col_name,...)

index_col_name : column_name[(length)][ASC | DESC]
  • unique:唯一索引
  • fulltext:全文索引
  • spatial:空间索引

示例:

CREATE INDEX PersonIndex
ON Person (LastName) 
CREATE INDEX PersonIndex
ON Person (LastName DESC) 

复合索引:

CREATE INDEX PersonIndex
ON Person (LastName, FirstName)

查看索引

show index from table_name;

会显示所有建在该表上的索引信息

删除索引

DROP INDEX index_name ON tbl_name;

老版本好像是

ALTER TABLE table_name DROP INDEX index_name;

示例:

DROP INDEX idx_city_name on city;

原创文章,作者:彭晨涛,如若转载,请注明出处:https://www.codetool.top/article/mysql%e7%b4%a2%e5%bc%95%e6%a6%82%e8%bf%b0%e5%8f%8a%e7%b4%a2%e5%bc%95%e7%9a%84%e5%88%86%e7%b1%bb/

发表评论

电子邮件地址不会被公开。