数据库之聚簇索引和非聚簇索引的区别
聚簇索引(密集索引)文件中的每个搜索码值都对应一个索引值(叶子节点不仅保存了键值,还保存了位于同一行记录中的其他列的信息),因为聚簇索引决定了表的物理排列顺序,而一个表只能有一种物理排列顺序,所以一个表只能创建一个聚簇索引。
非聚簇索引(稀疏索引)文件只为索引码的某些值建立索引项(叶子节点仅保存了键值以及该行数据的地址)。
数据库必须要有索引,没有索引则检索过程变成了顺序查找(全表扫描),O(n)的时间复杂度几乎是不能忍受的。我们非常容易想象出一个只有单关键字组成的表如何使用B+树进行索引,只要将这个关键字存储到B+树的节点即可。当数据库一条记录里包含多个字段时,一棵B+树就只能存储主键,如果检索的是非主键字段,则主键索引失去作用,又变成顺序查找(全表扫描)了。这时应该在第二个要检索的列上建立第二套索引(辅助键索引),这个索引由独立的B+树来组织。有两种常见的方法可以解决多个B+树访问同一套表数据的问题,一种叫做聚簇索引(数据和索引存储在一块,索引结构的叶子节点保存了行数据),一种叫做非聚簇索引(数据和索引分开存储,索引结构的叶子节点存储的是指向存放数据的物理块的指针)。
目前,MySQL数据库使用的比较主流的存储引擎有InnoDB和MyISAM,其中InnoDB存储引擎使用的是聚簇索引,而MyISAM存储引擎使用的是非聚簇索引。
InnoDB存储引擎使用聚簇索引并且有且仅有一个聚簇索引(因为聚簇索引决定了表的物理排列顺序,而一个表只能有一种物理排列顺序,因此一个表只能创建一个聚簇索引)。若有一个主键被定义,则该主键作为聚簇索引;若没有主键被定义,则该表的第一个唯一非空索引作为聚簇索引;如果不满足以上条件(没有定义主键,也没有合适的唯一索引),则该表会自动生成一个隐藏主键作为聚簇索引。InnoDB存储引擎必须要有一个主键,且该主键必须作为聚簇索引。
MyISAM存储引擎中的主键索引、唯一索引、普通索引都是非聚簇索引。
如图所示是一张包含2个列的表(以col1作为primary key):
在InnoDB存储引擎中,主键索引的B+树的叶子节点存储了主键值、事务ID(Transaction ID, TID)、回滚指针(Rollback Pointer, RP,用于支持事务和MVCC)以及该行记录中其他列的值。
在InnoDB存储引擎中,辅助键索引(二级索引)的B+树的叶子节点存储了Key字段和主键值。
在MyISAM存储引擎中,是按照列值和行号来组织索引的,其B+树的叶子节点存储了指向存放数据的物理块的指针。
InnoDB使用的是聚簇索引。将主键组织到一棵B+树(这棵B+树称为主键索引的B+树)中,这棵B+树的非叶子节点只存储了主键值,而行数据(主键的值和其他非主键的列的值)则存储在这棵B+树的叶子节点上,若使用"where id = 14"这样的搜索条件(过滤条件)查找主键(id列),则直接按照主键索引的B+树的检索算法即可查找到对应的叶子节点,之后获得整个行数据。若对Name列(非主键的列)进行条件搜索,则需要两个步骤:第一步在辅助键索引(二级索引)的B+树中检索Name,到达其叶子节点获取对应的主键值(id)。第二步使用找到的主键值(id)在主键索引的B+树中再执行一次B+树的检索操作,最终到达叶子节点即可获取对应的整个行数据。在InnoDB存储引擎中,主键索引的B+树的叶子节点存储了主键值(primary key column)、事务ID(transaction ID,TID)、回滚指针(rollback pointer,RP,用于支持事务和MVCC)和其他非主键的列的值;辅助键索引(二级索引)的B+树的叶子节点存储了Key字段和主键值。
MyISAM使用的是非聚簇索引。非聚簇索引的两棵B+树看上去没有什么不同,节点的结构完全一致,只是存储的内容不同而已。主键索引的B+树的节点存储了主键,辅助键索引(二级索引)的B+树的节点存储了辅助键。表数据存储在独立的地方,这两棵B+树的叶子节点都使用一个指针指向真正的表数据,对于表数据来说,这两个键(主键和辅助键)没有任何差别。由于这两棵索引树是独立的,通过辅助键索引(二级索引)无需访问主键索引的索引树。在MyISAM的存储引擎中,其主键索引和辅助键索引(二级索引)没有任何区别,都是按照列值和行号来组织索引的,在叶子节点处保存的都是指向存放数据的物理块的指针(数据的物理地址),其主键索引仅仅只是一个名为primary的唯一的非空的索引,且MyISAM可以不设置主键。从MyISAM存储的物理文件中我们也能看出,MyISAM存储引擎的索引文件(.MYI)和数据文件(.MYD)是相互独立的。