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 形式的主键索引(PrimaryKey),另外一种则是和其他存储引擎(如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),但并不会存放主键的键值信息。
2、Hash 索引
Hash 索引在MySQL 中使用的并不是很多,目前主要是Memory 存储引擎使用,而且在Memory 存储引擎中将Hash 索引作为默认的索引类型。所谓Hash 索引,实际上就是通过一定的Hash 算法,将需要索引的键值进行Hash 运算,然后将得到的Hash 值存入一个Hash 表中。然后每次需要检索的时候,都会将检索条件进行相同算法的Hash 运算,然后再和Hash 表中的Hash 值进行比较并得出相应的信息。
在Memory 存储引擎中,MySQL 还支持非唯一的Hash 索引。可能很多人会比较惊讶,如果是非唯一的Hash 索引,那相同的值该如何处理呢?在Memory 存储引擎的Hash 索引中,如果遇到非唯一值,存储引擎会将他们链接到同一个hash 键值下以一个链表的形式存在,然后在取得实际键值的时候时候再过滤不符合的键。
由于Hash 索引结构的特殊性,其检索效率非常的高,索引的检索可以一次定位,而不需要像BTree索引需要从根节点再到枝节点最后才能访问到页节点这样多次IO 访问,所以Hash 索引的效率要远高于B-Tree 索引。
可能很多人又会有疑问了,既然Hash 索引的效率要比B-Tree 高很多,为什么大家不都用Hash索引而还要使用B-Tree 索引呢?任何事物都是有两面性的,,Hash 索引也一样,虽然Hash 索引检索效率非常之高,但是Hash 索引本身由于其实的特殊性也带来了很多限制和弊端,主要有以下这些:
1). Hash 索引仅仅只能满足“=”,“IN”和“<=>”查询,不能使用范围查询;
由于Hash 索引所比较的是进行Hash 运算之后的Hash 值,所以Hash 索引只能用于等值的过滤,而不能用于基于范围的过滤,因为经过相应的Hash 算法处理之后的Hash 值的大小关系,并不能保证还和Hash 运算之前完全一样。
2). Hash 索引无法被利用来避免数据的排序操作;
由于Hash 索引中存放的是经过Hash 计算之后的Hash 值,而且Hash 值的大小关系并不一定和Hash 运算前的键值的完全一样,所以数据库无法利用索引的数据来避免任何和排序运算;
3). Hash 索引不能利用部分索引键查询;
对于组合索引,Hash 索引在计算Hash 值的时候是组合索引键合并之后再一起计算Hash 值,
而不是单独计算Hash 值,所以当我们通过组合索引的前面一个或几个索引键进行查询的时
候,Hash 索引也无法被利用到;
4). Hash 索引在任何时候都不能避免表扫面;
前面我们已经知道,Hash 索引是将索引键通过Hash 运算之后,将Hash 运算结果的Hash 值
和所对应的行指针信息存放于一个Hash 表中,而且由于存在不同索引键存在相同Hash 值的可能,所以即使我们仅仅取满足某个Hash 键值的数据的记录条数,都无法直接从Hash 索引中直接完成查询,还是要通过访问表中的实际数据进行相应的比较而得到相应的结果。
5). Hash 索引遇到大量Hash 值相等的情况后性能并不一定就会比B-Tree 索引高;
对于选择性比较低的索引键,如果我们创建Hash 索引,那么我们将会存在大量记录指针信息存与同一个Hash 值相关连。这样要定位某一条记录的时候就会非常的麻烦,可能会浪费非常多次表数据的访问,而造成整体性能的地下。
3、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 的创建所消耗的资源也是比较大的,所以在应用于实际生产环境之前还是尽量做好评估。
4、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 索引就无能为力了。