MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构。提取句子主干,就可以得到索引的本质:索引是数据结构。如果我们在数据库表中不添加索引,它的存储方式会是无序的存放在磁盘中,一行一行也会很整齐,可以想象以下Excel表格的样子。这个时候,就会引入索引的概念。
本文主要说明以下几个问题。
1、MySQL的索引采用什么样的数据结构?为什么采用该数据结构?
2、MySQL的索引类型及其实现?
3、如何在数据库中创建索引?
在我们使用我们数据库的时候,查询是我们使用的最主要的功能之一。我们想着怎么尽可能快的使我们查询的时候速度更快。最基本的查询方式就是顺序查找,这个也是最慢的,它的时间复杂度为O(n)。后来我们又知道了一些更牛的查找方式,比如二分查找(binary search)、二叉树查找(binary tree search),它们的时间复杂度分别为O(log2n)和O(nlog2n)。
因为我们的数据大部分是存储在磁盘的,很多时候我们的查找都需要读取磁盘,也就是所谓的IO,而磁盘的IO相较于CPU的处理速度,是非常慢的,所以我们要想尽一切办法减少磁盘的IO次数。对于我们数据库中数据存放,也就是想尽办法选择时间复杂度低的数据结构。而这个数据结构,也是我们索引的实现方式。
一、B+树索引
在MySQL中,我们常用的索引是B+树,具体相关树的实现和原理,可以参考前面的文章,本文只说结论。对于红黑树来树,它的高度太高,这样会增加IO的次数,而且由于黑红树的性质,导致逻辑上很近的节点(父子)物理上可能很远,无法利用局部性原理;而B树的特征我们知道,每个节点不仅仅只存放key,还会存放值,那么对于每一个节点,不能存放更多的key,减少非叶子节点的出度,从而是树的高度增加,从而降低性能。所以,最终,我们选择了B+树作为索引的数据结构。
此处需要说明的是,我们的节点是以页为单位的,一般以页的大小是16KB,也可以设置为4KB或者8KB。
我们的索引是基于B+树进行了优化,添加了指向相邻叶节点的指针,形成了带有顺序访问指针的B+树,这样做是为了提高区间的查找效率,只要找到第一个值那么就可以顺序的查找后面的值。B+树的结构如下:
下面,我们来看看具体的存储引擎是怎么实现索引结构的,MySQL中最常见的两种存储引擎分别是MyISAM和InnoDB,分别实现了非聚簇索引和聚簇索引(clustered index)。还有,我们可以按照索引的键是否为主键分为主索引(Primary index)和辅助索引(Secondary index)。
MyISAM索引实现
MyISAM引擎使用B+Tree作为索引结构,叶节点的data域存放的是数据记录的地址。下图是MyISAM索引的原理图:
这里设表一共有三列,假设我们以Col1为主键,则图8是一个MyISAM表的主索引(Primary key)示意。可以看出MyISAM的索引文件仅仅保存数据记录的地址。在MyISAM中,主索引和辅助索引(Secondary key)在结构上没有任何区别,只是主索引要求key是唯一的,而辅助索引的key可以重复。如果我们在Col2上建立一个辅助索引,则此索引的结构如下图所示:
同样也是一颗B+Tree,data域保存数据记录的地址。因此,MyISAM中索引检索的算法为首先按照B+Tree搜索算法搜索索引,如果指定的Key存在,则取出其data域的值,然后以data域的值为地址,读取相应数据记录。
MyISAM的索引方式也叫做“非聚集”的,之所以这么称呼是为了与InnoDB的聚集索引区分。
每个MyISAM在磁盘上存储成三个文件,分别为frm(存储表定义)、MYD(存储数据)、MYI(存储索引)。可以看到索引和数据是分开存放的。
InnoDB索引实现
虽然InnoDB也使用B+Tree作为索引结构,但具体实现方式却与MyISAM截然不同。
第一个重大区别是InnoDB的数据文件本身就是索引文件。从上文知道,MyISAM索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。而在InnoDB中,表数据文件本身就是按B+Tree组织的一个索引结构,这棵树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引。
上图是InnoDB主索引(同时也是数据文件)的示意图,可以看到叶节点包含了完整的数据记录。这种索引叫做聚集索引。因为InnoDB的数据文件本身要按主键聚集,所以InnoDB要求表必须有主键(MyISAM可以没有),如果没有显式指定,则MySQL系统会自动选择一个可以唯一标识数据记录的列作为主键,如果不存在这种列,则MySQL自动为InnoDB表生成一个隐含字段作为主键,这个字段长度为6个字节,类型为长整形。
第二个与MyISAM索引的不同是InnoDB的辅助索引data域存储相应记录主键的值而不是地址。换句话说,InnoDB的所有辅助索引都引用主键作为data域。例如,下图为定义在Col3上的一个辅助索引:
这里以英文字符的ASCII码作为比较准则。聚集索引这种实现方式使得按主键的搜索十分高效,但是辅助索引搜索需要检索两遍索引:首先检索辅助索引获得主键,然后用主键到主索引中检索获得记录。
了解不同存储引擎的索引实现方式对于正确使用和优化索引都非常有帮助,例如知道了InnoDB的索引实现后,就很容易明白为什么不建议使用过长的字段作为主键,因为所有辅助索引都引用主索引,过长的主索引会令辅助索引变得过大。再例如,用非单调的字段作为主键在InnoDB中不是个好主意,因为InnoDB数据文件本身是一颗B+Tree,非单调的主键会造成在插入新记录时数据文件为了维持B+Tree的特性而频繁的分裂调整,十分低效,而使用自增字段作为主键则是一个很好的选择。
二、Hash索引和全文索引
1、Hash索引
(1)概述及存储结构
主要就是通过Hash算法(常见的Hash算法有直接定址法、平方取中法、折叠法、除数取余法、随机数法),将数据库字段数据转换成定长的Hash值,与这条数据的行指针一并存入Hash表的对应位置;如果发生Hash碰撞(两个不同关键字的Hash值相同),则在对应Hash键下以链表形式存储。
检索算法:在检索查询时,就再次对待查关键字再次执行相同的Hash算法,得到Hash值,到对应Hash表对应位置取出数据即可,如果发生Hash碰撞,则需要在取值时进行筛选。目前使用Hash索引的数据库并不多,主要有Memory等。
(2)Hash索引的弊端
一般来说,索引的检索效率非常高,可以一次定位,不像B-Tree索引需要进行从根节点到叶节点的多次IO操作。有利必有弊,Hash算法在索引的应用也有很多弊端。
a、Hash索引仅仅能满足等值的查询,范围查询不保证结果正确。因为数据在经过Hash算法后,其大小关系就可能发生变化。
b、Hash索引不能被排序。同样是因为数据经过Hash算法后,大小关系就可能发生变化,排序是没有意义的。
c、Hash索引不能避免表数据的扫描。因为发生Hash碰撞时,仅仅比较Hash值是不够的,需要比较实际的值以判定是否符合要求。
d、Hash索引在发生大量Hash值相同的情况时性能不一定比B-Tree索引高。因为碰撞情况会导致多次的表数据的扫描,造成整体性能的低下,可以通过采用合适的Hash算法一定程度解决这个问题。
e、Hash索引不能使用部分索引键查询。因为当使用组合索引情况时,是把多个数据库列数据合并后再计算Hash值,所以对单独列数据计算Hash值是没有意义的。
2、全文索引
(1)概述
全文索引,MySQL中以前只有MyISAM存储引擎支持,但是InnoDB从1.2.x版本开始也支持全文索引。它用于替代效率较低的LIKE模糊匹配操作,而且可以通过多字段组合的全文索引一次性全模糊匹配多个字段。
(2)全文检索和顺序扫描的区别
所谓顺序扫描,比如要找内容包含某一个字符串的文件,就是一个文档一个文档的看,对于每一个文档,从头看到尾,如果此文档包含此字符串,则此文档为我们要找的文件,接着看下一个文件,直到扫描完所有的文件。如利用windows的搜索也可以搜索文件内容,只是相当的慢。
所谓全文检索就是先建立索引,再对索引进行搜索的过程就叫全文检索(Full-text Search)。全文检索通常使用倒排索引来实现。倒排索引和B+树索引一样,也是一种索引结构。简单的说,倒序索引的倒序,指的是这个索引是从关键词中查找对应的源,而不是从源中检索对应的关键词。
虽然创建索引的过程也是非常耗时的,但是索引一旦创建就可以多次使用,全文检索主要处理的是查询,所以耗时间创建索引是值得的。
三、索引基本语法
1、创建索引
创建表的时候添加索引
CREATE TABLE tablename(
id INT(10) PRIMARY KEY,
content TEXT,
name CHAR(10) NOT NULL,
[UNIQUE] INDEX name_index (name(4)))ENGION InnoDB DEFAULT CHARSE utf8;
在创建表以后添加索引
ALTER TABLE my_table ADD [UNIQUE] INDEX my_index(my_text);
CREATE INDEX my_index ON my_table(my_text);
2、根据索引查询
SELECT * FROM tablename WHERE MATCH(content) AGAINST('word1','word2',...);
3、删除索引
DROP INDEX my_index ON tablename;
4、查看表中的索引
SHOW INDEX FROM tablename;
最后就是总结了,下面这段话,如果理解了上面文章所说的,应该就能懂了!
索引是一个排序的列表,在这个列表中存储着索引的值和包含这个值的数据所在行的物理地址,在数据十分庞大的时候,索引可以大大加快查询的速度,这是因为使用索引后可以不用扫描全表来定位某行的数据,而是先通过索引表找到该行数据对应的物理地址然后访问相应的数据。同时,索引文件本身要消耗存储空间,而且会加重插入、删除和修改记录的负担,另外,MySQL在运行时也要消耗资源来维护索引,因此索引不是越多越好。一般下面两种情况不建议建索引,1、表记录比较少时;2、索引的选择性较低,所谓索引的选择性(selectivity),是指不重复的索引值(即基数/Cardinality,表示索引中不重复记录数量的预估值)与表记录数的比值。
参考文章
https://www.cnblogs.com/shan1393/p/8999622.html
http://blog.jobbole.com/105644/
http://blog.codinglabs.org/articles/theory-of-mysql-index.html
https://blog.csdn.net/tongdanping/article/details/79878302