在MySQL中,索引属于存储引擎级别的概念,不同存储引擎对索引的实现方式是不同的,本文主要讨论MyISAM和InnoDB两个存储引擎的索引实现方式。
索引的本质
MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构。
提取句子主干,就可以得到索引的本质:索引是数据结构。
MyISAM和InnoDB两个存储引擎的索引实现的数据结构都是B+树。
★聚簇索引和非聚簇索引
-
聚簇索引:聚簇顾名思义,聚集在一起,即索引和数据是存放同一个文件中,数据和索引都在1个以.ibd结尾的文件中。聚簇索引指的是索引项的排序方式和表中数据记录排序方式一致的索引。聚簇索引并不是一种单独的索引类型,而是一种数据存储方式。术语“聚簇”表示数据行和相邻的键值紧凑的存储在一起。也就是说聚集索引的顺序就是数据的物理存储顺序。它会根据聚集索引键的顺序来存储表中的数据,即对表的数据按索引键的顺序进行排序,然后重新存储到磁盘上。因为数据在物理存放时只能有一种排列方式,所以一个表只能有一个。叶子节点中存放的就是整张表的行记录数据,也将聚集索引的叶子节点称为数据页。主键索引就属于聚簇索引
-
非聚簇索引:索引文件和数据文件是分开的:.myd是数据文件.myi是索引树文件,叶子结点里面只有记录的磁盘文件地址,索引和数据是分开存储的。
聚簇索引
优点
查询速度非常快 :聚簇索引的查询速度非常的快,因为整个 B+树本身就是一颗多叉平衡树,叶子节点也都是有序的,定位到索引的节点,就相当于定位到了数据。相比于非聚簇索引, 聚簇索引少了一次读取数据的 IO 操作(因为聚簇索引索引和数据放一块,而非聚簇索引不放一块,找到了索引还要单独去磁盘里读取数据,多了一次I/O操作)
特别注意:聚簇索引对于主键的排序查找和范围查找速度非常快
缺点
依赖于有序的数据:因为 B+树是多路平衡树,如果索引的数据不是有序的,那么就需要在插入时排序,如果数据是整型还好,否则类似于字符串或 UUID 这种又长又难比较的数据,插入或查找的速度肯定比较慢。
更新代价大:如果对索引列的数据被修改时,那么对应的索引也将会被修改,而且聚簇索引的叶子节点还存放着数据,修改代价肯定是较大的,会导致频繁的页分裂。所以对于主键索引来说,主键一般都是不可被修改的。
非聚簇索引
优点
非聚簇索引的更新代价比聚簇索引要小。 非聚簇索引的叶子节点是不存放数据的。
缺点
1.依赖于有序的数据 跟聚簇索引一样,非聚簇索引也依赖于有序的数据
2.可能会二次查询(回表):这应该是非聚簇索引最大的缺点了,当查到索引对应的指针或主键后,可能还需要根据指针或主键再到数据文件或表中查询。
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的聚集索引区分。
非聚集索引的意思是数据和索引是分开的。
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的索引实现后,就很容易明白:
1.不建议使用过长的字段作为主键,因为所有辅助索引都引用主索引,过长的主索引会令辅助索引变得过大。
2.不建议使用用非单调的字段在InnoDB中作为主键,因为InnoDB数据文件本身是一颗B+Tree,非单调的主键会造成在插入新记录时数据文件为了维持B+Tree的特性而频繁的分裂调整,十分低效,而使用自增字段作为主键则是一个很好的选择。