索引,可以说是数据库相关优化尤其是在 Query 优化中最常用的优化手段之一了。但是很多人在大
部分时候都只是大概了解索引的用途,知道索引能够让 Query 执行的更快,而并不知道为什么会更快。
尤其是索引的实现原理,存储方式,以及不同索引之间的区别等就更不是太清楚了。正因为索引对我们
的 Query 性能影响很大,所以我们更应该深入理解 MySQL 中索引的基本实现,以及不同索引之间的区
别,才能分析出如何设计出最优的索引来最大幅度的提升 Query 的执行效率。
在 MySQL 中,主要有四种类型的索引,分别为:B-Tree 索引,Hash 索引,Fulltext 索引和 R-Tree 索引,下面针对这四种索引的基本实现方式及存储结构做一个大概的分析。
B-Tree 索引
B-Tree 索引是 MySQL 数据库中使用最为频繁的索引类型,除了 Archive 存储引擎之外的其他所有
的存储引擎都支持 B-Tree 索引。不仅仅在 MySQL 中是如此,实际上在其他的很多数据库管理系统中
B-Tree 索引也同样是作为最主要的索引类型,这主要是因为 B-Tree 索引的存储结构在数据库的数据检
索中有非常优异的表现。
一般来说,MySQL 中的 B-Tree 索引的物理文件大多都是以 Balance Tree 的结构来存储的,也就
是所有实际需要的数据都存放于 Tree 的 Leaf Node,而且到任何一个 Leaf Node 的最短路径的长度都
是完全相同的,所以我们大家都称之为 B-Tree 索引当然,可能各种数据库(或 MySQL 的各种存储引
擎)在存放自己的 B-Tree 索引的时候会对存储结构稍作改造。如 Innodb 存储引擎的 B-Tree 索引实
际使用的存储结构实际上是 B+Tree,也就是在 B-Tree 数据结构的基础上做了很小的改造,在每一个
Leaf Node 上面出了存放索引键的相关信息之外,还存储了指向与该 Leaf Node 相邻的后一个 Leaf
Node 的指针信息,这主要是为了加快检索多个相邻 Leaf Node 的效率考虑。
在 Innodb 存储引擎中,存在两种不同形式的索引,一种是 Cluster 形式的主键索引(Primary
Key),另外一种则是和其他存储引擎(如 MyISAM 存储引擎)存放形式基本相同的普通 B-Tree 索引,
这种索引在 Innodb 存储引擎中被称为 Secondary Index。下面我们通过图示来针对这两种索引的存放
形式做一个比较。
图示中左边为 Clustered 形式存放的 Primary Key,右侧则为普通的 B-Tree 索引。两种索引在
Root Node 和 Branch Nodes 方面都还是完全一样的。而 Leaf Nodes 就出现差异了。在 Primary Key
中,Leaf Nodes 存放的是表的实际数据,不仅仅包括主键字段的数据,还包括其他字段的数据,整个数
据以主键值有序的排列。而 Secondary Index 则和其他普通的 B-Tree 索引没有太大的差异,只是在
Leaf Nodes 出了存放索引键的相关信息外,还存放了 Innodb 的主键值。
所以,在 Innodb 中如果通过主键来访问数据效率是非常高的,而如果是通过 Secondary Index 来
访问数据的话,Innodb 首先通过 Secondary Index 的相关信息,通过相应的索引键检索到 Leaf Node
之后,需要再通过 Leaf Node 中存放的主键值再通过主键索引来获取相应的数据行。
MyISAM 存储引擎的主键索引和非主键索引差别很小,只不过是主键索引的索引键是一个唯一且非空
的键而已。而且 MyISAM 存储引擎的索引和 Innodb 的 Secondary Index 的存储结构也基本相同,主要
的区别只是 MyISAM 存储引擎在 Leaf Nodes 上面出了存放索引键信息之外,再存放能直接定位到
MyISAM 数据文件中相应的数据行的信息(如 Row Number),但并不会存放主键的键值信息。
Hash 索引
Hash 索引在 MySQL 中使用的并不是很多,目前主要是 Memory 存储引擎使用,而且在 Memory 存
储引擎中将 Hash 索引作为默认的索引类型。所谓 Hash 索引,实际上就是通过一定的 Hash 算法,将
需要索引的键值进行 Hash 运算,然后将得到的 Hash 值存入一个 Hash 表中。然后每次需要检索的时
候,都会将检索条件进行相同算法的 Hash 运算,然后再和 Hash 表中的 Hash 值进行比较并得出相应
的信息。
在 Memory 存储引擎中,MySQL 还支持非唯一的 Hash 索引。可能很多人会比较惊讶,如果是非唯
一的 Hash 索引,那相同的值该如何处理呢?在 Memory 存储引擎的 Hash 索引中,如果遇到非唯一
值,存储引擎会将他们链接到同一个 hash 键值下以一个 链表的形式存在,然后在取得实际键值的时候
时候再过滤不符合的键。
由于 Hash 索引结构的特殊性,其检索效率非常的高,索引的检索可以一次定位,而不需要像 B-Tree 索引需要从根节点再到枝节点最后才能访问到页节点这样多次 IO 访问,所以 Hash 索引的效率要远高于 B-Tree 索引。
可能很多人又会有疑问了,既然 Hash 索引的效率要比 B-Tree 高很多,为什么大家不都用 Hash
索引而还要使用 B-Tree 索引呢?任何事物都是有两面性的,,Hash 索引也一样,虽然 Hash 索引检
索效率非常之高,但是 Hash 索引本身由于其实的特殊性也带来了很多限制和弊端,主要有以下这些:
- Hash 索引仅仅只能满足“=”,“IN”和“<=>”查询,不能使用范围查询;
由于 Hash 索引所比较的是进行 Hash 运算之后的 Hash 值,所以 Hash 索引只能用于等值的
过滤,而不能用于基于范围的过滤,因为经过相应的 Hash 算法处理之后的 Hash 值的大小关
系,并不能保证还和 Hash 运算之前完全一样。 - Hash 索引无法被利用来避免数据的排序操作;
由于 Hash 索引中存放的是经过 Hash 计算之后的 Hash 值,而且 Hash 值的大小关系并不一定
和 Hash 运算前的键值的完全一样,所以数据库无法利用索引的数据来避免任何和排序运算; - Hash 索引不能利用部分索引键查询;
对于组合索引,Hash 索引在计算 Hash 值的时候是组合索引键合并之后再一起计算 Hash 值,
而不是单独计算 Hash 值,所以当我们通过组合索引的前面一个或几个索引键进行查询的时
候,Hash 索引也无法被利用到; - Hash 索引在任何时候都不能避免表扫面;
前面我们已经知道,Hash 索引是将索引键通过 Hash 运算之后,将 Hash 运算结果的 Hash 值
和所对应的行指针信息存放于一个 Hash 表中,而且由于存在不同索引键存在相同 Hash 值的
可能,所以即使我们仅仅取满足某个 Hash 键值的数据的记录条数,都无法直接从 Hash 索引
中直接完成查询,还是要通过访问表中的实际数据进行相应的比较而得到相应的结果。 - Hash 索引遇到大量 Hash 值相等的情况后性能并不一定就会比 B-Tree 索引高;
对于选择性比较低的索引键,如果我们创建 Hash 索引,那么我们将会存在大量记录指针信息
存与同一个 Hash 值相关连。这样要定位某一条记录的时候就会非常的麻烦,可能会浪费非常
多次表数据的访问,而造成整体性能的低下。
Full-text 索引
Full-text 索引也就是我们常说的全文索引,目前在 MySQL 中仅有 MyISAM 存储引擎支持,而且也
并不是所有的数据类型都支持全文索引。目前来说,仅有 CHAR,VARCHAR 和 TEXT 这三种数据类型的列可
以建 Full-text 索引。
一般来说,Fulltext 索引主要用来替代效率低下的 LIKE '%***%' 操作。实际上,Full-text 索引
并不只是能简单的替代传统的全模糊 LIKE 操作,而且能通过多字段组合的 Full-text 索引一次全模糊
匹配多个字段。
Full-text 索引和普通的 B-Tree 索引的实现区别较大,虽然他同样是以 B-Tree 形式来存放索引
数据,但是他并不是通过字段内容的完整匹配,而是通过特定的算法,将字段数据进行分隔后再进行的
索引。一般来说 MySQL 系统会按照四个字节来分隔。在整个 Full-text 索引中,存储内容被分为两部
分,一部分是分隔前的索引字符串数据集合,另一部分是分隔后的词(或者词组)的索引信息。
所以,Full-text 索引中,真正在 B-Tree 索引细细中的并不是我们表中的原始数据,而是分词之后的索引数
据。在 B-Tree 索引的节点信息中,存放了各个分隔后的词信息,以及指向包含该词的分隔前字符串信
息在索引数据集合中的位置信息。
Full-text 索引不仅仅能实现模糊匹配查找,在实现了基于自然语言的的匹配度查找。当然,这个
匹配读到底有多准确就需要读者朋友去自行验证了。Full-text 通过一些特定的语法信息,针对自然语
言做了各种相应规则的匹配,最后给出非负的匹配值。
此外,有一点是需要大家注意的,MySQL 目前的 Full-text 索引在中文支持方面还不太好,需要借
助第三方的补丁或者插件来完成。而且 Full-text 的创建所消耗的资源也是比较大的,所以在应用于实
际生产环境之前还是尽量做好评估。
R-Tree 索引
R-Tree 索引可能是我们在其他数据库中很少见到的一种索引类型,主要用来解决空间数据检索的问
题。
在 MySQL 中,支持一种用来存放空间信息的数据类型 GEOMETRY,且基于 OpenGIS 规范。在
MySQL5.0.16 之前的版本中,仅仅 MyISAM 存储引擎支持该数据类型,但是从 MySQL5.0.16 版本开始,
BDB,Innodb,NDBCluster 和 Archive 存储引擎也开始支持该数据类型。当然,虽然多种存储引擎都开
始支持 GEOMETRY 数据类型,但是仅仅之后 MyISAM 存储引擎支持 R-Tree 索引。
在 MySQL 中采用了具有二次分裂特性的 R-Tree 来索引空间数据信息,然后通过几何对象(MRB)
信息来创建索引。
虽然仅仅只有 MyISAM 存储引擎支持空间索引(R-Tree Index),但是如果我们是精确的等值匹
配,创建在空间数据上面的 B-Tree 索引同样可以起到优化检索的效果,空间索引的主要优势在于当我
们使用范围查找的时候,可以利用到 R-Tree 索引,而这时候,B-Tree 索引就无能为力了。
对于 R-Tree 索引的详细介绍和使用信息清参阅 MySQL 使用手册。