【译】MySQL InnoDB 排序索引构建

前言

不必了解 MySQL®Percona Server for MySQL 如何构建索引。 但是,如果您了解处理过程,那么当您想为数据插入保留适当数量的空间时,它可能会有所帮助。 从 MySQL 5.7 开始,开发人员改变了他们为 InnoDB 构建二级索引的方式,应用了自底向上而不是早期版本中使用的自顶向下方法。 在这篇文章中,我将通过一个示例来说明如何构建 InnoDB 索引。 最后,我将解释如何使用这种理解来为 innodb_fill_factor 设置一个合适的值。

索引构建过程

要在现有数据的表上建立索引,在 InnoDB 中有以下阶段:

  • 读取阶段(从聚集索引读取并构建二级索引条目)
  • 合并排序阶段
  • 插入阶段(将排序后的记录插入二级索引)

在 5.6 版本之前,MySQL 通过一次插入一条记录来构建二级索引。这是一种“自上而下”的方法。对插入位置的搜索从根(顶部)开始,并到达相应的叶页(向下)。记录插入到光标指向的叶页上。在查找插入位置以及进行页面拆分和合并(在根级别和非根级别)方面,成本很高。你怎么知道发生了太多的页面拆分和合并?您可以在我的同事 Marco Tusa 较早的博客中了解这一点,请点击此处。

从 MySQL 5.7 开始,添加索引期间的插入阶段使用“排序索引构建”,也称为“批量加载索引”。在这种方法中,索引是“自下而上”构建的。即叶页面(底部)首先构建,然后非叶级别直到根。

用例

在这些情况下使用排序索引构建:

* ALTER TABLE t1 ADD INDEX (or CREATE INDEX)
* ALTER TABLE t1 ADD FULLTEXT INDEX
* ALTER TABLE t1 ADD COLUMN, ALGORITHM=INPLACE
* OPTIMIZE TABLE t1

对于最后两个用例,ALTER 创建一个中间表。中间表索引(主键索引和二级索引)是使用“排序索引构建”构建的。

算法

  1. 在 Level 0创建一个页面。同时创建一个指向该页面的游标。
  2. 使用 Level 0 的游标插入数据到页面,直到填满。
  3. 一旦页面已满,创建一个兄弟页面(不插入数据到兄弟页面)。
  4. 为当前满页创建一个节点指针(子页中的最小键),并在上一层(父页)插入一个节点指针。
  5. 在上层,检查游标是否已经定位。如果没有,请为该级别创建一个父页面和一个游标。
  6. 在父页面插入节点指针。
  7. 如果父页面已满,请重复步骤 3、4、5、6。
  8. 现在插入兄弟页面并使游标指向兄弟页面。
  9. 在所有插入的末尾,每个级别都有一个指向最右侧页面的游标。提交所有游标(意味着提交修改页面的小事务,释放所有锁存器)。

为简单起见,上述算法跳过了有关压缩页面和处理 BLOB(外部存储的 BLOB)的细节。

自下而上构建索引的演示

使用一个示例,让我们看看如何构建二级索引,自下而上。再次为简单起见,假设叶页和非叶页中允许的最大记录数为 3。

CREATE TABLE t1 (a INT PRIMARY KEY, b INT, c BLOB);

INSERT INTO t1 VALUES (1, 11, 'hello111');
INSERT INTO t1 VALUES (2, 22, 'hello222');
INSERT INTO t1 VALUES (3, 33, 'hello333');
INSERT INTO t1 VALUES (4, 44, 'hello444');
INSERT INTO t1 VALUES (5, 55, 'hello555');
INSERT INTO t1 VALUES (6, 66, 'hello666');
INSERT INTO t1 VALUES (7, 77, 'hello777');
INSERT INTO t1 VALUES (8, 88, 'hello888');
INSERT INTO t1 VALUES (9, 99, 'hello999');
INSERT INTO t1 VALUES (10, 1010, 'hello101010');
ALTER TABLE t1 ADD INDEX k1(b);

InnoDB 将主键字段附加到二级索引。二级索引 k1 的记录格式为 (b, a)。在排序阶段之后,记录是:
(11,1), (22,2), (33,3), (44,4), (55,5), (66,6), (77,7), (88,8), (99,9), (1010, 10)。

初始插入阶段

让我们从记录 (11,1) 开始。

  1. 在 Level 0(叶级别)创建一个页面。
  2. 在页面上创建一个游标。
  3. 所有插入内容都会转到此页面,直到填满为止。
(11,1)插入

箭头显示游标当前指向的位置。它目前在第 5 页,下一个数据插入在此页。

还有两个空槽,因此插入记录 (22,2) 和 (33,3) 很简单。

(22,2) 和 (33,3) 插入

对于下一条记录 (44, 4),第 5 页已满。以下是步骤。

页面填满时的索引构建

  1. 创建兄弟页面 – 第 6 页。
  2. 暂时不要插入数据到兄弟页面。
  3. 提交游标处的页面,即迷你事务提交、释放闩锁等。
  4. 作为提交的一部分,创建一个节点指针并将其插入到父页面(current Level(0) + 1) 中。即在 Level 1。
  5. 节点指针的格式为(子页中的最小键,子页号)。第 5 页的最小键是 (11,1)。在父级插入记录 ((11,1),5)。
  6. Level 1 的父页面尚不存在。 MySQL 创建第 7 页和指向第 7 页的游标。
  7. 将 ((11,1),5) 插入第 7 页。
  8. 现在,返回到 Level 0 并创建从第 5 页到第 6 页的链接,反之亦然。
  9. Level 0 的游标现在指向兄弟页第 6 页。
  10. 将 (44,4) 插入第 6 页。
(44,4) 插入

下一个插入 - (55,5) 和 (66,6) - 很简单,在第 6 页。

(55,5) 和 (66,6)插入

记录 (77,7) 的插入与 (44,4) 类似,只是父页面(第 7 页)已经存在并且它有空间容纳另外两条记录。先将节点指针 ((44,4),6) 插入第 7 页,然后将 (77,7) 记录到同级第 8 页中。


(77,7) 插入

插入记录 (88,8) 和 (99,9) 很简单,因为第 8 页有两个空槽。

(88,8) 和 (99,9)插入

下一个插入 (1010, 10)。将节点指针 ((77,7),8) 插入到级别 1 的父页面(第 7 页)。

MySQL 在 Level 0 创建兄弟页 9。将记录 (1010,10) 插入第 9 页并将游标更改为该页。

(1010,10) 插入

提交所有级别的游标。在上面的示例中,数据库在级别 0 提交第 9 页,在级别 1 提交第 7 页。我们现在有一个自下而上构建的完整 B+-树索引!

索引填充因子

全局变量 innodb_fill_factor 设置 Btree 索引页面中用于插入的空间量。默认值为 100,表示使用整个页面(不包括页眉、页尾)。聚集索引具有 innodb_fill_factor = 100 的豁免。在这种情况下,1/16 的聚集索引页面空间保持空闲。 也就是说。 6.25% 的空间是为未来的 DML 保留的。

innodb_fill_factor 值 80 意味着 MySQL 使用 80% 的页面用于插入,并留下 20% 用于将来的更新。
如果 innodb_fill_factor 为 100,则没有剩余空间可用于将来插入二级索引。如果您希望在添加索引后表上有更多的 DML,DML 可能会导致页面拆分以及再次合并。在这种情况下,建议使用 80-90 之间的值。此变量值还会影响OPTIMIZE TABLEALTER TABLE DROP COLUMN、ALGORITHM=INPLACE索引语句的索引重建。

您不应该使用太低的值——例如低于 50——因为索引会占用更多的磁盘空间。值越低,索引中的页面就越多,索引统计信息采样可能不是最佳的。优化器可能会选择具有次优统计信息的错误查询计划。

排序索引构建的优势

  1. 没有页面拆分(不包括压缩表)和合并。
  2. 没有重复查找插入位置(页面反复读取,IO较多)。
  3. 插入不会记录 redo 日志(页面分配除外),因此重做日志子系统的压力较小。

缺点

没有……嗯,好的,有一个,值得单独发表一篇博文敬请关注!

【原文地址】:MySQL InnoDB Sorted Index Builds

你可能感兴趣的:(【译】MySQL InnoDB 排序索引构建)