建立了非聚簇索引主键的表,在执行插入操作时,为什么比建立聚簇索引主键表要快
1、主键是一种唯一约束,因此在插入数据前,必须要先检测当前插入的这条记录中的主键字段值,是否已存在于既有数据中;
2、数据库管理系统内部是存在查询优化器的,它会根据查询的条件与目标表的组织结构来选择效率最高的查询手法,因此同样的语句,在不同情况下实现算法可能是不一样的;
3、索引以B+树形式存储(B树是二叉树,B-树是3节点多路查找树,B+树让叶子节点指向相邻的其它叶子上的节点,B*树就不只是叶子节点,连非叶子节点也指向其它同级的非叶子节点);
4、索引信息(其它也是一行一行的记录)存储在索引页上(只存储数据记录中的索引字段值以及字段值与所在记录行地址的对应关系,相对要窄一些),数据记录存储于数据页上(存储数据记录中的每一个字段值,相对要较宽一些),索引页小,不过是因为它只保存每条记录的这一字段,所以会小,如果数据表只有一列,那索引页和数据页的大小将会是相同的;
5、聚集索引页和非聚集索引页中的字段值都是排好序的,它们的叶子节点上保存的都是索引字段值与所在数据表记录地址的对应关系,只不过非聚集索引必须一 一对应,一个值一个叶子节点,这个值重复N次,就有N个节点,只是对应的地址不同而已,而聚集索引因为索引字段值与数据表记录地址顺序的一致性,它不并不需要一一对应,在第7点中还会提及。在非聚集索引上“覆盖,连接,交叉和过滤”四种技术,而索引覆盖正是基于非聚集索引页包含此字段所有的值这一点。
6、无论索引字段值是否重复,非聚集索引的叶子节点上已经保存了所有的该字段值,因为即使字段值有重复,每个值也会多次出现在不同的索引页的叶子节点中,也是一个值对应一个地址,每个一 一对应关系形成一个叶节点,虽然非聚集索引,再向上一级的节点是字段值,在这一层,每一个不同的值只在这个层级的节点中出现一次,是看不出字段值是否重复的
7、而聚集索引的索引页中,叶子节点恰恰就只到这一级,因为索引字段顺序与数据表记录地址顺序的一致性,它在叶子节点这一级所记录的该值所对应的地址,已经可以通过地址累加找到所有的其它连续值(假如没有唯一约束,还能找到重复值),以下一聚集索引字段值所对应的地址作为累加的终止判断条件,所以它不需要为连续字段值(或重复值)建立一 一对应关系(索引值与记录物理地址保持连续性一致即未必是完全的内存连续填充哦),这样索引页就能更小啊,这样的优化策略,肿么可能没有采用呢,一定是采用了的,所以,聚集索引中很有可能并不包含此索引字段值的所有内容(因此有的文章称聚集索引中的内容相对索引字段的在所有记录行中的所有内容只是一个中间表,也就是说不能完全代表~);
8、因此,在非聚集索引中,判断既有索引字段值与准备新加入的索引字段值是否存在重复,无论是否唯一,在索引页内都可以完成判断,因为数据表在这个字段上的所有值都存在于非聚集索引的索引页中了,索引页比数据页只小不大,那在这样的条件下,优化器当然知道只需要在索引页中干活,不必跑到数据表里去折腾啦(如果是字段值有唯一约束,那我想优化器在优化时甚至不需要找到最底层的叶子节点,找到倒第二层就足够判断的了,对于设计数据库的大牛们来说,他们当然也清楚这一点,那么他们设计的查询优化器里肯定就会有这样的优化策略呗);
9、好,我们再来看聚集索引,聚集索引页中的叶子并不能确保所有重复值出现(见第7条的分析),那么只在它的索引页中去判断该字段值与新值是否存在重复,那是不准确的,那怎么办呢?优化器如果只在索引页中折腾也是无计可施了,那咱们也就只能去原始数据表里找了呗,好么,原始数据表每遍历一条记录可是所有字段的宽度之和哦,这下在查找过程中的内存消耗可就大了,这就是在判断新值与既有值是否存在重复的过程中,为什么在该字段上有非聚集索引 比 有聚集索引要快的原因,因为优化器很聪明,它从来、一直知道怎么着查询最快,关键看你给人家创造什么样的背景条件了;
10、网上许多讨论这个问题的帖子,也都是认为效率的差异主要就产生在这个“判断将要插入的新值是否已经存在于既有值中”的环节中,核心也就是说聚集索引页中无法100%保证包含所有索引字段的内容,即使主键给了它唯一约束,因为聚集索引的概念包含了压缩优化的思想,反而使得查询优化器无法仰仗这压缩过的索引数据鸟~不过这倒确实是个挺有意思的讨论话题,说得不对的地方,欢迎批评指正~