MySQL官方对索引的定义是:索引(Index)是帮助MySQL高效获取数据的数据结构。索引最形象的比喻就是图书的目录。注意只有在大量数据中查询时索引才显得有意义。
在MySQL中索引是在存储引擎层实现的,而不是在服务器层实现的,所以不同存储引擎具有不同的索引类型和实现。常见的索引分类如下:
按数据结构分类:B+tree索引、Hash索引、Full-text索引。
按物理存储分类:聚集索引、非聚集索引(也叫二级索引、辅助索引)。
按字段特性分类:主键索引(PRIMARY KEY)、唯一索引(UNIQUE)、普通索引(INDEX)、全文索引(FULLTEXT)。
按字段个数分类:单列索引、联合索引(也叫复合索引、组合索引)。
按数据结构分类
MySQL索引按数据结构分类可分为: B+tree索引、Hash索引、Full-text索引 。
InnoDB | MyISAM | Memory | |
---|---|---|---|
B+tree索引 | √ | √ | √ |
Hash索引 | × | × | × |
Full-text索引 | √(MySQL5.6.4) | √ | × |
注:InnoDB实际上也支持Hash索引,但是InnoDB中Hash索引属于是自适应Hash索引,它的创建过程由存储引擎引擎自动优化创建,不能人为干预是否为表创建Hash索引
B+tree 索引是MySQL中被存储引擎采用最多的索引类型。它适用于全键值、键值范围和最左前缀查找。使用联合索引时,如果不是按照索引列的顺序进行查找,则无法使用索引。除了适用于查找,还可以用于排序和分组。
-
B+tree索引
在MySQL中为什么会选用B+tree做索引结构呢?
B+tree 是在B树基础上的一种优化,其更适合做存储索引结构。在 B+tree 中,非叶子节点上仅存储键值,不存储数据;而所有数据记录均存储在叶子节点上,并且数据是按照顺序排列的。此外在 B+tree 中各个数据页之间是通过双向链表连接的,叶子节点中的数据是通过单向链表连接的。B+tree 的结构图如下:
B+tree 结构实现数据索引具有如下优点:a. 非叶子节点上可以存储更多的键值,相应的树的阶数(节点的子节点树)就会更大,树也就会变得更矮更胖。这样一来我们查找数据进行磁盘I/O的次数就会大大减少,数据查询的效率也会更快。
b. 所有数据记录都有序存储在叶子节点上,就会使得范围查找,排序查找,分组查找以及去重查找变得异常简单。
c. 数据页之间、数据记录之间都是通过链表链接的,有了这个结构的支持就可以方便的在数据查询后进行升序或者降序操作。
-
Hash索引
Memory引擎默认支持哈希索引,如果多个Hash值相同,出现哈希碰撞,那么索引就以链表方式存储。(这有点类似于Java中的HashMap,哈哈~)InnoDB或MyISAM存储引擎页支持Hash索引,但是需要通过伪Hash索引来实现,叫自适应Hash索引。Hash索引是基于Hash算法实现的,如上图所示,我们将一系列的最终的键值通过哈希函数转化为存储实际数据桶的地址数值。值本身存储的地址就是基于哈希函数的计算结果,而搜索的过程就是利用哈希函数从元数据中推导出桶的地址。(对Hash算法不太理解就往这里看:HashMap核心知识-深度学习) 所以基于Hash索引结构的特殊性,其检索效率非常高,索引的检索可以一次定位,不像B+Tree索引需要从根节点到枝节点,最后才能访问到页节点这样多次的I/O访问,所以Hash索引在精确查询时的效率要远高于B+Tree索引。虽然Hash索引效率高,但是Hash索引本身由于其特殊性也带来了很多限制和弊端,主要有以下这些:
a. Hash索引仅仅能满足等值查询,不能进行范围查询
由于Hash索引比较的是进行Hash运算之后的Hash值,所以它只能用于等值的过滤,不能用于基于范围的过滤,因为经过相应的Hash算法处理之后的Hash值的大小关系,并不能保证和Hash运算前完全一样。b. Hash索引无法通过操作索引来排序
由于 Hash索引中存放的是经过 Hash 计算之后的Hash值,而且Hash值的大小关系并不一定和Hash运算前的键值完全一样,所以数据库无法利用索引的数据来避免任何排序运算。c. 组合Hash索引不能利用部分索引键进行查询
对于组合Hash索引,索引在计算 Hash 值的时候是组合索引键合并后再一起计算 Hash 值,而不是单独计算 Hash值,所以通过组合索引的前面一个或几个索引键进行查询的时候,Hash 索引也无法被利用。d. Hash索引依然需要回表扫描
Hash索引是将索引键通过 Hash 运算之后,将Hash运算结果的Hash值和所对应的行指针信息存放于一个 Hash 表中,由于不同索引键可能存在相同 Hash 值,所以即使取满足某个 Hash 键值的数据的记录条数,也无法从Hash索引中直接完成查询,还是要通过访问表中的实际数据进行相应的比较,并得到相应的结果。e. Hash索引遇到大量Hash值相等的情况后性能并不一定就会比B+Tree索引高
区分度低的索引键(如,性别),如果创建Hash索引,那么将会存在大量记录指针信息与同一个Hash值相关联。这样要定位某一条记录时就会非常麻烦,会浪费多次表数据的访问,而造成整体性能低下。 -
Full-text索引
Full-text索引一般使用倒排索引实现。倒排索引同B+tree索引一样,也是一种索引结构。MySQL中InnoDB存储引擎在之前版本中是不支持全文检索的,要使用全文检索的话只能使用MySIAM存储引擎。在 MySQL 5.6.4 版本中InnoDB存储引擎才开始支持Full-text索引。
对于文本类型的大对象,或者较大的CHAR类型的数据,如果使用普通索引,那么匹配文本前几个字符还是可行的,但是想要匹配文本中间的几个单词,那么就要使用LIKE %word%来匹配,这样需要很长的时间来处理,响应时间会大大增加,这种情况,就可使用时FULLTEXT索引了,在生成FULLTEXT索引时,会为文本生成一份单词的清单,在索引时及根据这个单词的清单来索引。
Full-text索引的查询有自己特殊的语法,而不能使用 LIKE 模糊查询的语法,语法如下:
SELECT * FROM table_name MATCH(ft_index) AGAINST('查询字符串');
按物理存储分类
MySQL索引按叶子节点存储的是否为完整表数据分为: 聚集索引、非聚集索引(也叫二级索引、辅助索引) 。
-
聚簇索引
聚簇索引就是按照每张表的主键构造一颗 B+tree,同时叶子节点中存放的就是整张表的行记录数据,聚集索引的叶子节点被称为数据页。
InnoDB表要求必须有聚簇索引,默认在主键字段上建立聚簇索引,在没有主键字段的情况下,表的第一个非空的唯一索引将被建立为聚簇索引,在前两者都没有的情况下,InnoDB将自动生成一个隐式的自增id列,并在此列上建立聚簇索引。 -
非聚集索引(也叫二级索引、辅助索引)
非聚集索引的结构和聚集索引基本相同(非叶子结点存储的都是索引指针),区别在于叶子节点存放的不是行数据而是数据主键。因此在使用非聚集索引进行查找时,需要先查找到主键值,然后再到聚集索引中进行查找。
两种索引的区别:每个索引上包含的字段内容不同,聚集索引包含所有真实的物理数据,非聚集索引只包含索引字段和主键字段。此外,聚集索引一个表只能有一个,而非聚集索引一个表可以存在多个。那非聚集索引这种查询方式算不算回表呢?
回表查询简单来说就是通过非聚集索引查询数据时,得不到完整的数据内容,需要再次查询主键索引来获得数据内容。
所以如果使用非聚集索引后还需要使用其他字段的(包括在where条件中或者select子句中),则需要通过主键索引回表到聚集索引获取其他字段。如果是非聚集索引可以满足SQL语句的所有字段的,则被称为全覆盖索引,没有回表开销。
避免回表查询问题,常见的方式就是建立联合索引(组合索引),实现索引覆盖,从而避免回表查询。索引覆盖就是指索引的叶子节点已经包含了查询的数据,满足查询要求,没必要再回表进行查询。
按字段特性分类
MySQL索引按字段特性分类可分为: 主键索引(PRIMARY KEY)、唯一索引(UNIQUE)、普通索引(INDEX)、全文索引(FULLTEXT) 。
-
主键索引(PRIMARY KEY)
建立在主键上的索引被称为主键索引,一张数据表只能有一个主键索引,索引列值不允许有空值,通常在创建表时一起创建。 -
唯一索引(UNIQUE)
建立在UNIQUE字段上的索引被称为唯一索引,一张表可以有多个唯一索引,索引列值允许为空,列值中出现多个空值不会发生重复冲突。 -
普通索引(INDEX)
建立在普通字段上的索引被称为普通索引。 -
全文索引(FULLTEXT)
MyISAM 存储引擎支持Full-text索引,用于查找文本中的关键词,而不是直接比较是否相等。Full-text索引一般使用倒排索引实现,它记录着关键词到其所在文档的映射。InnoDB 存储引擎在 MySQL 5.6.4 版本中也开始支持Full-text索引。
按索引字段个数分类
MySQL索引按字段个数分类可分为: 单列索引、联合索引(也叫复合索引、组合索引) 。
-
单列索引
建立在单个列上的索引被称为单列索引。 -
联合索引(复合索引、组合索引)
建立在多个列上的索引被称为联合索引,又叫复合索引、组合索引。在MySQL中使用联合索引时要遵循 最左前缀匹配原则 。所以我们需要注意如下几个方面:a. 实际业务场景中创建联合索引时,我们应该把识别度比较高的字段放在前面,提高索引的命中率,充分的利用索引。
b. 创建联合索引后,该索引的任何最左前缀都可以用于查询。比如当你有一个联合索引(col1, col2, col3),该索引的所有最左前缀为(col1)、(col1, col2)、(col1, col2, col3),包含这些列的所有查询都会使用该索引进行查询。
c. 虽然联合索引可以避免回表查询,提高查询速度,但同时也会降低表数据更新的速度。因为联合索引列更新时,MySQL不仅要保存数据,还要维护一下索引文件。所以不要盲目使用,应根据业务需求来创建。