以MySQL为例,详解数据库索引原理及深度优化( 五 )


MyISAM的索引方式也叫做“非聚集”的,之所以这么称呼是为了与InnoDB的聚集索引区分 。
5.2 InnoDB索引实现
虽然InnoDB也使用B+Tree作为索引结构,但具体实现方式却与MyISAM截然不同 。
第一个重大区别是InnoDB的数据文件本身就是索引文件 。从上文知道,MyISAM索引文件和数据文件是分离的,索引文件仅保存数据记录的地址 。而在InnoDB中,表数据文件本身就是按B+Tree组织的一个索引结构,这棵树的叶节点data域保存了完整的数据记录 。这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引 。

以MySQL为例,详解数据库索引原理及深度优化

文章插图
上图是InnoDB主索引(同时也是数据文件)的示意图,可以看到叶节点包含了完整的数据记录 。这种索引叫做聚集索引 。因为InnoDB的数据文件本身要按主键聚集,所以InnoDB要求表必须有主键(MyISAM可以没有),如果没有显式指定,则MySQL系统会自动选择一个可以唯一标识数据记录的列作为主键,如果不存在这种列,则MySQL自动为InnoDB表生成一个隐含字段作为主键,这个字段长度为6个字节,类型为长整形 。
第二个与MyISAM索引的不同是InnoDB的辅助索引data域存储相应记录主键的值而不是地址 。换句话说,InnoDB的所有辅助索引都引用主键作为data域 。例如,下图为定义在Col3上的一个辅助索引:
以MySQL为例,详解数据库索引原理及深度优化

文章插图
这里以英文字符的ASCII码作为比较准则 。聚集索引这种实现方式使得按主键的搜索十分高效,但是辅助索引搜索需要检索两遍索引:首先检索辅助索引获得主键,然后用主键到主索引中检索获得记录 。
了解不同存储引擎的索引实现方式对于正确使用和优化索引都非常有帮助,例如知道了InnoDB的索引实现后,就很容易明白为什么不建议使用过长的字段作为主键,因为所有辅助索引都引用主索引,过长的主索引会令辅助索引变得过大 。再例如,用非单调的字段作为主键在InnoDB中不是个好主意,因为InnoDB数据文件本身是一颗B+Tree,非单调的主键会造成在插入新记录时数据文件为了维持B+Tree的特性而频繁的分裂调整,十分低效,而使用自增字段作为主键则是一个很好的选择 。
六、索引使用策略及优化
MySQL的优化主要分为结构优化(Scheme optimization)和查询优化(Query optimization) 。本章讨论的高性能索引策略主要属于结构优化范畴 。本章的内容完全基于上文的理论基础,实际上一旦理解了索引背后的机制,那么选择高性能的策略就变成了纯粹的推理,并且可以理解这些策略背后的逻辑 。
6.1 联合索引及最左前缀原理
联合索引(复合索引)
首先介绍一下联合索引 。联合索引其实很简单,相对于一般索引只有一个字段,联合索引可以为多个字段创建一个索引 。它的原理也很简单,比如,我们在(a,b,c)字段上创建一个联合索引,则索引记录会首先按照A字段排序,然后再按照B字段排序然后再是C字段,因此,联合索引的特点就是:
  • 第一个字段一定是有序的
  • 当第一个字段值相等的时候,第二个字段又是有序的,比如下表中当A=2时所有B的值是有序排列的,依次类推,当同一个B值得所有C字段是有序排列的
| A | B | C |
| 1 | 2 | 3 |
| 1 | 4 | 2 |
【以MySQL为例,详解数据库索引原理及深度优化】| 1 | 1 | 4 |
| 2 | 3 | 5 |
| 2 | 4 | 4 |
| 2 | 4 | 6 |
| 2 | 5 | 5 |
其实联合索引的查找就跟查字典是一样的,先根据第一个字母查,然后再根据第二个字母查,或者只根据第一个字母查,但是不能跳过第一个字母从第二个字母开始查 。这就是所谓的最左前缀原理 。
最左前缀原理
我们再来详细介绍一下联合索引的查询 。还是上面例子,我们在(a,b,c)字段上建了一个联合索引,所以这个索引是先按a 再按b 再按c进行排列的,所以:
以下的查询方式都可以用到索引
  •  
select * from table where a=1;
select * from table where a=1 and b=2;
select * from table where a=1 and b=2 and c=3;
上面三个查询按照 (a ), (a,b ),(a,b,c )的顺序都可以利用到索引,这就是最左前缀匹配 。
如果查询语句是:
  •  
select * from table where a=1 and c=3; 那么只会用到索引a 。
如果查询语句是:
  •  
select * from table where b=2 and c=3;因为没有用到最左前缀a,所以这个查询是用户到索引的 。


推荐阅读