没有索引的查找
- 上节我们知道了数据是怎么存储的,数据被分成一个个页,然后页与页之间是根据双向列表来进行连接的,页中的记录是根据单向列表来进行连接的,并且将主键生成页目录。
- 根据这个规则我们查找对应的记录数据,就有可能分成两种方式:
- 第一种,就是不通过查找主键的记录数据时,我们只能通过遍历每个页数据,并且遍历每个页的单向链表表来查找对应的记录数据
- 第二种,就是通过查找主键的记录数据,我们还是需要遍历每个页的数据,但是我只需要遍历每个页页目录来查找对应的记录数据
- 毫无疑问以上的方式,对于记录少的时候问题不大,但是数据量一旦过大,就会有很大的性能问题。
- 那这个方法怎么解决呢?
- 这个时候就需要引出索引的概念
索引
- 还是用上节的那一个表,记录也是上节那些,假设现在页号是8,结构如下图:
CREATE TABLE page_test ( p1 INT, p2 INT, p3 VARCHAR ( 10000 ), PRIMARY KEY ( p1 ) ) CHARSET = ascii ROW_FORMAT = Compact;
- 上一节也说到,页与页之间,页号是不一定连续,也就是说当前我们的页号是8,可能下一条记录插入的时候,产生新的页,页号可能就是36,如下图
- 但是通过上图也能得知,记录与记录之间,主键一定是由小到大的关系,也就是说,下一个数据页中的数据记录的主键值一定大于上一个数据页中的数据记录主键的值。有了这个前提,我们就可以有一个大胆的想法
- 假设我们如果把页的第一个记录的主键,当作一条记录的主键,然后页号当作值,把每个页号当作记录存到一个页里面,是不是可以大大加快主键的查询,如下图
- 然后我们的查询流程就变成,假设我们要找到主键值为5的记录:
- 先从页号26中根据二分法找出主键值为5在页号为36的数据页中
- 然后在从36的数据页再做一次二分法,找到主键值为5的记录数
- 至此我们就通过两次二分查找,找到对应主键为5的记录数
- 为了实现上面那种想法,之前 record_type 就排上用场了,通过上节知道 record_type 有 4个值,当为1的时候 就说明这个记录是索引记录,并且为了能够跟普通记录区分开,InnoDB 做了一下操作:
- 目录项记录的record_type值是1
- 目录项记录只有主键值和页的编号两个列
- 只有在存储目录项记录的页中的主键值最小的目录项记录的min_rec_mask值为1,其他别的记录的min_rec_mask值都是0
- 上面解决了怎么存放索引记录的问题,但还有一个问题,InnoDB是使用页来作为管理存储空间的基本单位,也就是最多能保证16KB的连续存储空间,而随着表中记录数量的增多,需要非常大的连续的存储空间才能把所有的目录项都放下,这对记录数量非常多的表是不现实的,所以当一个表中的数据太多,以至于数据页太多,一个数据页存放不下怎么办呢?
- 最简单的方法是,再加一个数据页存放,但是这样又回到之前的问题,又需要遍历索引数据页,顺序查找,那现在又怎么办呢?
- 聪明的人已经想到了,再来一层,把这些索引数据页在来一层,结构就变成下面:
- 这个结构就是传说中的树
B+ 树索引
- 准确的说,InnoDB的结构是B+树,不论是存放用户记录的数据页,还是存放目录项记录的数据页,我们都把它们存放到B+树这个数据结构中了,所以我们也称这些数据页为节点。从图中可以看出来,我们的实际用户记录其实都存放在B+树的最底层的节点上,这些节点也被称为叶子节点或叶节点,其余用来存放目录项的节点称为非叶子节点或者内节点,其中B+树最上面的那个节点也称为根节点。
- 基本上树的一般都只有4层,假设我们一个数据页的记录数是100,当树只有1层时,我们可以存放100条,当树有2层时,我们可以存放100 x 100 = 10000 记录,当树有3层时,我们可以存放 100 x 100 x 100 = 1000000 记录,当树有4层时,我们可以存放 100 x 100 x 100 x 1000 = 100000000条记录,就已经很多了。
- 而我们根据索引去查找数据,只需要要通过4次二分查找就能找到对应的数据
聚簇索引
- InnoDB 存储引擎会自动的为我们创建聚簇索引,也就是说聚簇索引就是InnoDB的存储方式。现在有个问题什么是聚簇索引?
- 聚簇索引需要满足两个条件:
- 使用记录的存放逻辑是根据主键的大小来进行排序的,具体规则:
- 页内的记录是按照主键的大小顺序排成一个单向链表。
- 各个存放用户记录的页也是根据页中用户记录的主键大小顺序排成一个双向链表。
- 存放目录项记录的页分为不同的层次,在同一层次中的页也是根据页中目录项记录的主键大小顺序排成一个双向链表。
- B+ 树的叶子节点存储的是完整的记录数据
- 使用记录的存放逻辑是根据主键的大小来进行排序的,具体规则:
二级索引
- 以上索引大概思路差不多讲完了,但此时还有一个很大的问题,就是我们之前都是建立在用户只用主键进行搜索的情况下来创建,但是假设他要根据别的字段进行建立索引呢?或者说一张表会有多个索引怎么办呢?
- 我们先来自己想想,一般情况下,怎么做,如果是我就会根据我们想要创建索引的字段,再建造一个B+树,就是说把索引字段当作主键创建一颗树,就像下面这样
- 那现在这个表就会存两个B+树,使用哪个索引就查那个表。
- 这样虽然解决了,但是我觉得数据存两份,冗余太多了,有没有简便的方案。有没有可能 非主键 的 B+ 树,底层的叶子节点不存数据记录,只存主键,然后通过找到主键再从主键 B+ 树找到对应的数据。
- 事实上人家InnoDB就是这么干的,而这些非主键的B+树的索引被称之为二级索引或者辅助索引