前言

当了解记录存储的行格式与数据页的结构后,再学习B+树索引就比较简单了。

InnoDB 数据页的7个组成部分,各个数据页可以组成一个 双向链表 ,而每个数据页中的记录会按照主键值从小到大的顺序组成一个 单向链表 ,每个数据页都会为存储在它里边儿的记录生成一个 页目录 。在通过主键查找某条记录的时候,可以在页目录中使用二分法快速定位到对应的槽,然后再遍历该槽对应分组中的记录即可快速找到指定的记录。
image.png
其中页a、页b、页c … 页n 这些页可以不在物理结构上相连,只要通过双向链表相关联的逻辑结构。

一、没有索引如何查找

如果需要查找条件为对某个列精确匹配的情况,该如何查找呢?

SELECT [列名列表] FROM 表名 WHERE 列名 = xxx;

所谓精确匹配,就是搜索条件中用等于 = 连接起的表达式。

1.1 在一个页中的查找

目前表中的记录比较少,所有的记录都可以被存放到一个页中,在查找记录的时候可以根据搜索条件的不同分为两种情况:

  • 以主键为搜索条件

在页目录中使用二分法快速定位到对应的槽,然后再遍历该槽对应分组中的记录即可快速找到指定的记录。

  • 以其他列作为搜索条件

非主键列没有构造页目录,所以无法通过二分法来实现查找。只能从最小记录开始依次遍历单链表中的每条记录,然后对比每条记录是不是符合搜索条件。

1.2 在很多页中的查找

表中的记录较多的时候,需要用许多的页来装记录。与只在一个页里面查找记录相比,在许多页中查找记录需要先定位到记录所在页,剩下的步骤与在一个页中查找记录相同。但如果没有索引的话,只能从第一个页沿着双向链表一直往下找,每个页遍历的方法与1.1的方法相同。

二、索引

首先,建立一个表,规定c1为主键,指定使用行格式Compact存储记录:

CREATE TABLE index_demo(
    c1 INT,
    c2 INT,
    c3 CHAR(1),
    PRIMARY KEY(c1)
) ROW_FORMAT = Compact;

简画一下index_demo的行格式:
image.png

  • record_type :记录头信息的一项属性,表示记录的类型, 0 表示普通记录、2 表示最小记录、3 表示最
    大记录、1 为索引;
  • next_record :记录头信息的一项属性,表示下一条地址相对于本条记录的地址偏移量,为了方便大家理
    解,我们都会用箭头来表明下一条记录是谁。
  • 各个列的值:这里只记录在index_demo 表中的三个列,分别是c1 、c2 和c3 。
  • 其他信息:除了上述3种信息以外的所有信息,包括其他隐藏列的值以及记录的额外信息。
    image.png

2.1 一个简单的索引方案

前面说到,当在很多个页中查找记录的时候,只能从第一页依次遍历,只有找到记录所在页之后才能找到该记录。这样效率太低了,那如何解决呢?前面已经知道,在一页里面,为所有记录分组,然后通过页目录的槽位实现快速定位到每组中的最大记录。查考页目录的设计,为了快速定位到记录所在页,可不可以把许多的页分组,然后通过一个这样的“目录”快速定位记录所在页呢?当然可以,实现这个关于页的目录的功能的结构是索引。

索引的建立需要完成:

  • 下一个数据页中用户记录的主键值必须大于上一个页中用户记录的主键值;
  • 给所有的页建立一个目录项。

2.1.1 主键值要求

假设一个数据页只能存三条用户记录,当然实际可以存很多条。这里插入三条数据:

INSERT INTO index_demo VALUES(1, 4, 'u'), (3, 9, 'd'), (5, 3, 'y');

这些记录在一个数据页里,按照主键值的大小串联成一个单向链表,如图所示:
从图中可以看出, index_demo 表中的3条记录都被插入到了编号为10 的数据页中,且数据页容量已经满了。此时我们再来插入一条记录:

INSERT INTO index_demo VALUES(4, 4, 'a');

因为容量已满,此时需要分配一个新的数据页:image.png

新分配的数据页编号可能并不是连续的,也就是说我们使用的这些页在存储空间里可能并不挨着。它们只是通过维护着上一个页和下一个页的编号而建立了链表关系。另外, 页10 中用户记录最大的主键值是5 ,而页28 中有一条记录的主键值是4 ,因为5 > 4 ,所以这就不符合下一个数据页中用户记录的主键值必须大于上一个页中用户记录的主键值的要求,所以在插入主键值为4 的记录的时候需要伴随着一次记录移动,也就是把主键值为5 的记录移动到页28 中,然后再把主键值为4 的记录插入到页10 中,这个过程的示意图如下:
image.png

从图中看出,对页中的记录进行增删改操作的过程中,必须通过一些诸如记录移动的操作来始终保证主键值满足要求:下一个数据页中用户记录的主键值必须大于上一个页中用户记录的主键值。这个过程称为 页分裂

2.1.2 建立页的目录项

由于数据页的编号可能并不是连续的,在向index_demo 表中插入许多条记录后,效果可能是这样:
image.png

因为这些16KB 的页在物理存储上可能并不挨着,所以如果想从这么多页中根据主键值快速定位某些记录所在的页,就需要给它们做个目录,每个页对应一个目录项,每个目录项包括下边两个部分:

  • 页的 用户记录中最小的主键值 ,用key 来表示;
  • 页号,用page_no 表示。

例子中的页目录设计如下:
image.png

以页28 为例,它对应目录项2 ,这个目录项中包含着该页的页号28 以及该页中用户记录的最小主键值5 。我们只需要把几个目录项在物理存储器上连续存储,比如把他们放到一个数组里,就可以实现根据主键值快速查找某条记录的功能了。

比方说我们想找主键值为20 的记录,具体查找过程分两步:

  • 先从目录项中根据二分法快速确定出主键值为20 的记录在目录项3 中( 因为12 < 20 < 209 ),它对应的页是页9 。
  • 再根据前边说的在页中查找记录的方式去页9 中定位具体的记录。

至此,针对数据页做的简易目录搞定。这个目录有一个别名,称为索引。

2.2 InnoDB中的索引方案

2.2.1 B+树索引的结构

上边是一个简易的索引方案,前提是假设所有目录项都可以在物理存储器上连续存储,可以根据主键值进行查找,使用二分法快速定位具体的目录项。但是这样做有几个问题:

  • InnoDB 是使用页来作为管理存储空间的基本单位,也就是最多能保证16KB 的连续存储空间,而随着表中记录数量的增多,需要非常大的连续的存储空间才能把所有的目录项都放下,这对记录数量非常多的表是不现实的。
  • 增加或删除记录的时候,把某一页的记录全部删掉,那就意味着这一页对应的目录项没有用了, 它后面目录项都要前移,很费时间。

为了解决这些问题,可以复用之前存储用户记录的 数据页来存储目录项 ,为了和用户记录做一下区分,把这些用来表示目录项的记录称为 目录项记录 。而区分用户记录与目录项记录通过记录头信息里的record_type 属性,record_type 属性有0、1、2和3四个值:0 代表普通的用户记录;1 代表目录项记录;2 代表最小记录;3代表最大记录。

把前边使用到的目录项放到数据页中的样子就是这样:
image.png

从图中可以看出来,重新分配了一个编号为30 的页来专门存储目录项记录。

这里需要注意目录项记录与普通用户记录的不同点:

  • 目录项记录的record_type 值是1,而普通用户记录的record_type 值是0;
  • 目录项记录只有主键值和页的编号两个列,而普通的用户记录的列包括额外的记录的额外信息(根据定义的行格式确定)与记录的真实数据(隐藏列+真实数据);
  • 记录头信息中的min_rec_mask 属性,只有在存储目录项记录的页中的主键值最小的目录项记录的min_rec_mask 值为1 ,其他别的记录的min_rec_mask 值都是0 。

除了上述几点,其他方面差不多都一样。比如:都是16KB大小,页结构也一样由七个部分组成,都会为主键生成页目录等。

有了存储目录项记录的页,现在如果要查找某主键对应的记录,步骤可分为:

  • 先在目录项记录的页里通过二分法查找主键值,然后通过主键值对应的页号,找到记录所在页;
  • 再在对应的数据页里,通过二分法找到相应的槽位,通过槽位找到记录分组,最后遍历该分组找到主键值对应的数据。

比如,我们要找主键值为20的记录,因为20的主键值12 < 20 < 200,所以找到主键值12对应的页9。然后,在页9里,找到主键值为20的记录。这里数据较少,只有一个分组,所以不需要二分法遍历槽位。

当数据较多的时候,就如上图中,假如有五个用户记录数据页,且目录项记录数据页只能存放四条记录,这时候一个目录项记录数据页装不下,该怎么办呢?当然是,再添加一个目录项记录数据页。比如,再向上图中插入一条主键值为320 的用户记录的话,那就需要分配一个新的存储目录项记录的页:
image.png
从图中可以看出,插入了一条主键值为320 的用户记录之后需要两个新的数据页:

  • 为存储该用户记录而新生成了页31 。
  • 因为原先存储目录项记录的页30 的容量已满(前边假设只能存储4条目录项记录),所以不得不需要一个新的页32 来存放页31 对应的目录项。

虽然,这样设计是装下了所有用户记录与目录项记录,但如果数据真的超级超级多的时候,目录项记录的数据页也会变得很多,不止是上图中的页30与页32两个。那这个时候,要查找一条用户记录的时候,需要先顺序遍历每个目录项记录的数据页,这样也会很慢。如何解决?在一个用户记录的数据页里,通过分组,在页目录Page Directory里设计槽位来对应每一个分组。

这里采用另一种方法, 为这些存储目录项记录的页再生成一个更高级的目录 ,就像是一个多级目录一样,大目录里嵌套小目录,小目录里才是实际的数据,所以现在各个页的示意图就是这样子:
image.png
如图,生成了一个存储更高级目录项的页33 ,这个页中的两条记录分别代表页30 和页32 ,如果用户记录的主键值在[1, 320) 之间,则到页30 中查找更详细的目录项记录,如果主键值大于等于320 的话,就到页32中查找更详细的目录项记录。

这样的数据结构,简化一下:
image.png
注意,图中同一层是双向链表, 最底层只有最下面那一层是双向链表 。这时候,如果倒立着看这张图,就很像一棵树。可以看出, 一个数据页代表树中的一个节点 。图中,最上面的代表数据范围更广的目录项记录数据页,称为根。中间的目录项记录数据页称为非叶子节点。而最底层的用户记录数据页,是真正存放数据的数据页,称为叶子节点。这样存储数据的结构,称为 B+树 。规定,最底层(存放用户记录的那层)为第0层。

2.2.2 聚簇索引

上面介绍的B+ 树本身就是一个目录,或者说本身就是一个索引。它有两个特点:

  • 使用记录主键值的大小进行记录和页的排序。这要求:

    • 页内的用户记录是按照主键的大小顺序排成一个单向链表;
    • 各个存放用户记录的页也是根据页中用户记录的主键大小顺序排成一个双向链表;
    • 存放目录项记录的页分为不同的层次,在同一层次中的页也是根据页中目录项记录的主键大小顺序排成
      一个双向链表。
  • B+ 树的叶子节点存储的是完整的用户记录。

把具有这两种特性的B+ 树称为 聚簇索引所有完整的用户记录都存放在这个聚簇索引的叶子节点处 。这种聚簇索引并不需要在MySQL 语句中显式的使用INDEX 语句去创建,InnoDB 存储引擎会自动的创建聚簇索引。另外有趣的一点是,在InnoDB 存储引擎中, 聚簇索引就是数据的存储方式(所有的用户记录都存储在了叶子节点),也就是所谓的索引即数据,数据即索引。

2.2.3 二级索引

上边介绍的聚簇索引只能在搜索条件是主键值时才能发挥作用,因为B+ 树中的数据都是按照主键进行排序的。如果以别的列作为搜索条件,就需要再创建一棵B+树,而这颗B+树的叶子节点存放主键值。比如,用c2 列的大小作为数据页、页中记录的排序规则,这里c2列可称为索引列,再建一棵B+ 树如图:
image.png

这个B+树与聚簇索引有几处不同:

  • 使用记录c2 列的大小进行记录和页的排序。这包括三个方面的含义:

    • 页内的记录是按照c2 列的大小顺序排成一个单向链表;
    • 各个存放用户记录的页也是根据页中记录的c2 列大小顺序排成一个双向链表;
    • 存放目录项记录的页分为不同的层次,在同一层次中的页也是根据页中目录项记录的c2 列大小顺序排成一个双向链表。
  • B+ 树的 叶子节点存储的并不是完整的用户记录,而只是c2列+主键这两个列的值
  • 目录项记录中不再是主键+页号的搭配,而变成了c2列+页号的搭配。

此时,如果我们想找到c2列值为4的记录。就需要:

首先,确定目录项记录页;然后,通过目录项记录页确定用户记录真实所在的页;再在真实存储用户记录的页中定位到具体的记录;但是这个B+ 树的叶子节点中的记录只存储了c2 和c1 (也就是主键)两个列,所以必须 再根据主键值去聚簇索引中再查找一遍完整的用户记录 。这里,根据非主键列(2级索引)查找到主键值,再通过主键值去聚簇索引中查找用户记录的过程称为 回表 。这种按照非主键列建立的B+树需要一次回表操作才可以定位到完整的用户记录,所以这种B+ 树也被称为 二级索引 ,或者 辅助索引

思考一下,为什么要回表,而不是直接将用户记录放在c2列B+树的叶子节点呢?是因为,如果将用户记录直接放在叶子节点,相当于每建立一棵B+ 树都需要把所有的用户记录再都拷贝一遍,太浪费存储空间。

需要注意一点,c1与c2列都是数字,所以可以用来排序。那可以对存储字符串的列建立索引吗?是可以的,因为有字符集与字符集的比较规则,所以字符串也可以比较大小。

2.2.4 联合索引

我们也可以同时以多个列的大小作为排序规则,也就是同时为多个列建立索引。比方说我们想让B+ 树按照c2和c3 列的大小进行排序,这个包含两层含义:

  • 先把各个记录和页按照c2 列进行排序;
  • 在记录的c2 列相同的情况下,采用c3 列进行排序。

为c2 和c3 列建立的索引的示意图如下:

image.png

这里需要注意几点:

  • 每条目录项记录都由c2 、c3 、页号这三个部分组成,各条记录先按照c2 列的值进行排序,如果记录的c2 列相同,则按照c3 列的值进行排序;
  • B+ 树叶子节点处的用户记录由c2 、c3 和主键c1 列组成。

这种 以多个列的大小为排序规则建立的B+树称为联合索引,本质上也是一个二级索引 。联合索引有一个很重要的知识点,叫做最左前缀原则以及索引下推:

a.最左前缀原则

当需要频繁的查找某一列的数据时,在设计联合索引的时候,需要将该索引列放到最左边。这是因为MySQLl建立多列索引(联合索引)有最左前缀的原则,即最左优先,比如:

  • 如果有一个2列的索引(c1,c2),则已经对(c1)、(c1,c2)上建立了索引;
  • 如果有一个3列索引(c1,c2,c3),则已经对(c1)、(c1,c2)、(c1,c2,c3)上建立了索引;

范围查询 (类似查询条件为:c1 > 4 and c2 = 20 and c3 = 'e' 这种 ):

  • 范围列可以用到索引(必须是最左前缀),但是范围列后面的列无法用到索引。同时,索引最多用于一个范围列,因此如果查询条件中有两个范围列则无法全用到索引,比如这里的 c2 = 20 and c3 = 'e' 就失效了。
  • mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配。比如上面举例中,c2与c3都失效。但如果调整c1,c2,c3列的顺序,建立(c2,c3,c1)的索引,则都可以用到。注意: like aa%后模糊查询索引有效,like %aa模糊查询索引无效 。最左前缀索引也只能用在一个范围列里。
  • =和in可以乱序,比如c1 = 1 and c2 = 2 and c3 = 3 建立(c1,c2,c3)索引可以任意顺序,mysql的查询优化器会帮你优化成索引可以识别的形式。

设计联合索引的原则是:1.较少维护——如果通过调整顺序,可以少维护一个索引,那么这个顺序往往就是需要优先考虑采用的;2.考虑空间——采取(a,b)、(b) 还是 (b,a)、(a)这种格式呢? 如:有两个字段,若name 字段是比 age 字段大的 ,那建议创建一个(name,age) 的联合索引和一个 (age) 的单字段索引。

b.索引下推

索引下推(Index Condition Pushdown),简称 ICP。 是Mysql 5.6版本引入的技术优化。旨在 在“仅能利用最左前缀索的场景” 下(说明前面有范围查询,导致不能使用联合索引里的全部索引列),对不在最左前缀索引中的其他联合索引字段加以利用。在遍历索引时,就用这些其他字段进行过滤(比如下图中的 age )。过滤会减少遍历索引查出的主键条数,从而减少回表次数,提示整体性能。

—— 如果查询利用到了索引下推ICP技术,在Explain输出的Extra字段中会有“Using index condition”。即代表本次查询会利用到索引,且会利用到索引下推。

——索引下推技术的实现:在遍历索引的那一步,由只传入可以利用到的字段值,改成了多传入下推字段值。

索引不下推的情况:
image.png
索引下推:
image.png

2.3 InnoDB的B+树索引的注意事项

2.3.1 根页面万年不动窝

前边介绍B+ 树索引的时候,为了大家理解上的方便,先把存储用户记录的叶子节点都画出来,然后接着画存储目录项记录的内节点,实际上B+ 树的形成过程是这样的:

  • 每当为某个表创建一个B+ 树索引(聚簇索引不是人为创建的,默认就有)的时候,都会为这个索引创建一个根节点页面。最开始表中没有数据的时候,每个B+ 树索引对应的根节点中既没有用户记录,也没有目录项记录。
  • 随后向表中插入用户记录时,先把用户记录存储到这个根节点中。
  • 当根节点中的可用空间用完时继续插入记录,此时会将根节点中的所有记录复制到一个新分配的页,比如页a 中,然后对这个新页进行页分裂的操作,得到另一个新页,比如页b 。这时新插入的记录根据键值(也就是聚簇索引中的主键值,二级索引中对应的索引列的值)的大小就会被分配到页a 或者页b 中,而根节点便升级为存储目录项记录的页。

注意, 一个B+树索引的根节点创建之后,便不会再移动,也就是页号不会改变

2.3.2 内节点中目录项记录的唯一性

B+ 树索引的内节点中目录项记录的内容是索引列 + 页号的搭配,但是这个搭配对于二级索引来说有点儿不严谨。还拿index_demo 表为例,假设这个表中的数据是这样的:

c1c2c3
11‘u’
31‘d’
51‘y’
71‘a’

如果二级索引中目录项记录的内容只是索引列 + 页号的搭配的话,那么为c2 列建立索引后的B+ 树应该长这样:

image.png
如果想新插入一行记录,其中c1 、c2 、c3 的值分别是: 9 、1 、‘c’ ,那么在修改这个为c2 列建立的二级索引对应的B+ 树时便碰到了个大问题:由于页3 中存储的目录项记录是由c2列 + 页号的值构成的,页3 中的两条目录项记录对应的c2 列的值都是1 ,而新插入的这条记录的c2 列的值也是1 ,那这条新插入的记录到底应该放到页4中,还是应该放到页5 中呢?MySQL自己也不确定。

为了让新插入记录能找到自己在那个页里,需要保证在B+树的 同一层内节点的目录项记录除页号这个字段以外是唯一的 。所以对于二级索引的内节点的目录项记录的内容实际上是由三个部分构成的:

  • 索引列的值
  • 主键值
  • 页号

也就是把主键值也添加到二级索引内节点中的目录项记录,这样就能保证B+ 树每一层节点中各条目录项记录除页号这个字段外是唯一的,所以为c2 列建立二级索引后的示意图实际上应该是这样子的:
image.png
这样再插入记录(9, 1, ‘c’) 时,由于页3 中存储的目录项记录是由c2列 + 主键 + 页号的值构成的,可以先把新记录的c2 列的值和页3 中各目录项记录的c2 列的值作比较,如果c2 列的值相同的话,可以接着比较主键值,因为B+ 树同一层中不同目录项记录的c2列 + 主键的值肯定是不一样的,所以最后肯定能定位唯一的一条目录项记录,在本例中最后确定新记录应该被插入到页5 中

2.3.3 一个页面最少存储2条记录

如果一个大的目录中只存放一个子目录,那就会导致目录层级非常非常多,而且最后的那个存放真实数据的目录中只能存放一条记录。这样不仅会导致查询效率慢,而且会浪费存储空间。为了避免这种情况,一个页面最好最少存储两条记录。

2.4 MyISAM中的索引方案简单介绍

前面说过,InnoDB 中索引即数据,也就是聚簇索引的那棵B+树的叶子节点中已经把所有完整的用户记录都包含了,而MyISAM 的索引方案虽然也使用树形结构,但是却将索引和数据分开存储:

  • 将表中的记录按照记录的插入顺序单独存储在一个文件中,称之为 数据文件 。这个文件并不划分为若干个数据页,有多少记录就往这个文件中塞多少记录。然后可以通过行号而快速访问到一条记录。

MyISAM 记录也需要记录头信息来存储一些额外数据,以index_demo 表为例,这个表中的记录使用MyISAM 作为存储引擎在存储空间中的表示:

image.png

由于在插入数据的时候并没有刻意按照主键大小排序,所以并不能在这些数据上使用二分法进行查找。

  • 使用MyISAM 存储引擎的表会把索引信息另外存储到另一个文件中,称为 索引文件

MyISAM 会单独为表的主键创建一个索引,只不过在索引的叶子节点中存储的不是完整的用户记录,而是主键值 + 行号的组合。也就是先通过索引找到对应的行号,再通过行号去找对应的记录!

这一点和InnoDB 是完全不相同的,在InnoDB 存储引擎中,只需要根据主键值对聚簇索引进行一次查找就能找到对应的记录,而在MyISAM 中却需要进行一次回表操作,意味着MyISAM 中建立的索引相当于全部都是二级索引!

  • 如果有需要的话,也可以对其它的列分别建立索引或者建立联合索引,原理和InnoDB 中的索引差不多,不过在叶子节点处存储的是相应的 列 + 行号 。这些索引也全部都是二级索引。

三、索引的创建与删除语句

InnoDB 和MyISAM 会自动为主键或者声明为UNIQUE 的列去自动建立B+ 树索引,但是如果想为其他的列建立索引就需要显式的去指明。

  • 在创建表的时候指定建立索引的单个列或者建立联合索引的多个列:
CREATE TALBE 表名 (
    -- 各种列的信息
    -- 这里的 KEY 和 INDEX 是同义词,任意选用一个就可以
    [KEY|INDEX] 索引名 (需要被索引的单个列或多个列)
)
  • 在修改表结构的时候添加索引:
ALTER TABLE 表名 ADD [INDEX|KEY] 索引名 (需要被索引的单个列或多个列);
  • 也可以在修改表的时候删除索引:
ALTER TABLE 表名 DROP [INDEX|KEY] 索引名;

举个栗子:

-- 创建
CREATE TABLE index_demo(
    c1 INT,
    c2 INT,
    c3 CHAR(1),
    PRIMARY KEY(c1),
    INDEX idx_c2_c3 (c2, c3)
);
-- 删除
ALTER TABLE index_demo DROP INDEX idx_c2_c3;

创建的索引名是idx_c2_c3 ,这个名称可以随便起,不过建议以idx_ 为前缀,后边跟着需要建立索引的列名,多个列名之间用下划线_ 分隔开。

总结

  • 数据页:InnoDB 数据页的7个组成部分,各个数据页可以组成一个双向链表,而每个数据页中的记录会按照主键值从小到大的顺序组成一个单向链表,每个数据页都会为存储在它里边儿的记录生成一个页目录。在通过主键查找某条记录的时候,可以在页目录中使用二分法快速定位到对应的槽,然后再遍历该槽对应分组中的记录即可快速找到指定的记录。
  • 索引:就是一种为了提高数据查询速度而设计的一种数据结构。
  • 建立索引的要求:下一个数据页中用户记录的主键值必须大于上一个页中用户记录的主键值;给所有的页建立一个 目录项
  • B+树索引结构:一个B+树的节点就是一张数据页。最顶层的节点,代表数据范围更广的目录项记录数据页,称为根。中间的目录项记录数据页称为非叶子节点,同一层的非叶子节点会形成一个双向链表。而最底层的用户记录数据页,是真正存放数据的数据页,称为叶子节点。完整的用户记录都存储在B+树的叶子节点中,在叶子节点的最下面会形成一个双向链表。
  • InnoDB索引分类:

    • 聚簇索引:以主键值的大小作为页和记录的排序规则,在叶子节点存储的是表中的所有列(真实数据);
    • 二级索引(辅助索引):以索引列的大小作为页和记录的排序规则,在叶子节点存储的是索引列+主键;
    • 联合索引:同时以多个列的大小作为排序规则,叶子节点存储的是索引列+主键,本质上也是一个二级索引。
  • B+树索引注意事项:

    • 根页面万年不动窝:根节点一旦创建就不会再移动;
    • 保证内节点中目录项记录的唯一性;
    • 一个页面最少存储2条记录,以避免存储空间浪费。
  • MyISAM存储引擎:采用数据和索引分开存储这种存储引擎的索引全部是二级索引,叶子节点存储的是列+主键。
最后修改:2022 年 05 月 15 日
如果觉得我的文章对你有用,请随意赞赏