MySQL5.7 InnoDB 磁盘结构之索引Index

  官网地址:MySQL :: MySQL 5.7 Reference Manual :: 14.6.2.1 Clustered and Secondary Indexes

欢迎关注留言,我是收集整理小能手,工具翻译,仅供参考,笔芯笔芯.

MySQL 5.7 参考手册  /  ...  /  聚集索引和二级索引
14.6.2.1 聚集索引和二级索引

每个InnoDB表都有一个特殊的索引,称为聚集索引,用于存储行数据。通常,聚集索引与主键同义。为了从查询、插入和其他数据库操作中获得最佳性能,了解如何InnoDB使用聚集索引来优化常见的查找和 DML 操作非常重要。

  • PRIMARY KEY当您在表上 定义 a 时,InnoDB将其用作聚集索引。应为每个表定义一个主键。如果没有逻辑唯一且非空的列或列集来使用主键,请添加自动增量列。自动增量列值是唯一的,并且会在插入新行时自动添加。

  • 如果您没有PRIMARY KEY为表定义 a,InnoDB则使用第一个 UNIQUE索引并将所有键列定义为NOT NULL聚集索引。

  • 如果表没有索引PRIMARY KEY或没有合适的 UNIQUE索引,则生成一个以包含行 ID 值的合成列命名InnoDB 的隐藏聚集索引 。GEN_CLUST_INDEX行按分配的行 ID 排序InnoDB。行 ID 是一个 6 字节字段,随着新行的插入而单调增加。因此,按行 ID 排序的行实际上是按插入顺序排列的。

聚集索引如何加速查询

通过聚集索引访问行的速度很快,因为索引搜索直接指向包含行数据的页面。如果表很大,则与使用与索引记录不同的页存储行数据的存储组织相比,聚集索引架构通常可以节省磁盘 I/O 操作。

二级索引与聚集索引的关系

聚集索引以外的索引称为二级索引。在 中InnoDB,辅助索引中的每条记录都包含该行的主键列以及为辅助索引指定的列。 InnoDB使用此主键值在聚集索引中搜索行。

如果主键较长,则二级索引会占用更多空间,因此主键较短是有利的。

有关利用InnoDB 聚集索引和二级索引的指南,请参阅 第 8.3 节“优化和索引”。

MySQL 5.7 参考手册  /  ...  /  InnoDB 索引的物理结构
14.6.2.2 InnoDB索引的物理结构

除空间索引外,InnoDB 索引都是B 树数据结构。空间索引使用 R 树,它是用于索引多维数据的专用数据结构。索引记录存储在其 B 树或 R 树数据结构的叶页中。索引页的默认大小为 16KB。innodb_page_size页大小由MySQL实例初始化时的设置决定 。请参见 第 14.8.1 节“InnoDB 启动配置”。

当新记录插入InnoDB 聚集索引时, InnoDB尝试留出 1/16 的页以供将来插入和更新索引记录。如果按顺序(升序或降序)插入索引记录,则生成的索引页大约为 15/16 已满。如果记录以随机顺序插入,则页数从 1/2 到 15/16 已满。

InnoDB创建或重建 B 树索引时执行批量加载。这种索引创建方法称为排序索引构建。该 innodb_fill_factor变量定义在排序索引构建期间填充的每个 B 树页面上的空间百分比,剩余空间保留用于将来的索引增长。空间索引不支持排序索引构建。有关更多信息,请参阅 第 14.6.2.3 节“排序索引构建”。设置 innodb_fill_factor为 100 时,会在聚集索引页中留下 1/16 的空间以供将来的索引增长使用。

如果索引页的填充因子InnoDB低于MERGE_THRESHOLD50%(如果未指定,则默认为 50%),InnoDB则尝试收缩索引树以释放该页。该 MERGE_THRESHOLD设置适用于 B 树和 R 树索引。有关更多信息,请参见 第 14.8.12 节 “配置索引页的合并阈值”。

MySQL 5.7 参考手册  /  ...  /  排序索引构建
14.6.2.3 排序索引构建

InnoDB创建或重建索引时执行批量加载,而不是一次插入一条索引记录。这种索引创建方法也称为排序索引构建。空间索引不支持排序索引构建。

索引构建分为三个阶段。在第一阶段, 扫描聚集索引,生成索引条目并将其添加到排序缓冲区中。当排序缓冲区已满时,条目将被排序并写入临时中间文件。这个过程也称为 “运行”。在第二阶段,将一次或多次运行写入临时中间文件,对文件中的所有条目执行合并排序。在第三个也是最后一个阶段,排序后的条目被插入到 B 树中。

在引入排序索引构建之前,索引条目是使用插入 API 一次将一条记录插入到 B 树中的。此方法涉及打开 B 树 游标以查找插入位置,然后使用 乐观插入将条目插入到 B 树页面中。如果由于页面已满而导致插入失败, 则将执行悲观插入,这涉及打开 B 树游标并根据需要拆分和合并 B 树节点以为条目找到空间。这种“自上而下”的弊端建立索引的方法是搜索插入位置的成本和B树节点的不断分裂和合并。

排序索引构建使用“自下而上”建立索引的方法。通过这种方法,B 树的所有级别都会保存对最右侧叶页的引用。分配必要 B 树深度的最右侧叶页,并根据条目的排序顺序插入条目。一旦叶页已满,节点指针就会附加到父页,并为下一次插入分配同级叶页。此过程将持续进行,直到插入所有条目,这可能会导致插入到根级别。当分配兄弟页时,对先前固定的叶页的引用将被释放,新分配的叶页将成为最右侧的叶页和新的默认插入位置。

为未来索引增长保留 B 树页面空间

要为将来的索引增长留出空间,可以使用该 innodb_fill_factor变量保留一定百分比的 B 树页面空间。例如,设置 innodb_fill_factor为 80 在排序索引构建期间保留 B 树页面中 20% 的空间。此设置适用于 B 树叶页和非叶页。它不适用于用于 TEXT或 BLOB条目的外部页面。保留的空间量可能与配置不完全一样,因为该 innodb_fill_factor值被解释为提示而不是硬限制。

排序索引构建和全文索引支持

全文索引支持排序索引 构建。以前,SQL 用于将条目插入全文索引。

排序索引构建和压缩表

对于压缩表,以前的索引创建方法将条目附加到压缩页和未压缩页。当修改日志(代表压缩页上的可用空间)变满时,压缩页将被重新压缩。如果由于空间不足而压缩失败,页面将被分割。通过排序索引构建,条目仅附加到未压缩的页面。当未压缩的页面已满时,就会对其进行压缩。自适应填充用于确保大多数情况下压缩成功,但如果压缩失败,则会拆分页面并再次尝试压缩。此过程一直持续到压缩成功为止。有关 B 树页面压缩的更多信息,请参阅 第 14.9.1.5 节 “InnoDB 表压缩如何工作”。

排序索引构建和重做日志记录

在排序索引构建期间禁用重做日志记录。相反,有一个 检查点来确保索引构建能够承受意外退出或失败。检查点强制将所有脏页写入磁盘。在排序索引构建期间,页面清理线程会定期收到信号以刷新 脏页,以确保可以快速处理检查点操作。通常,当干净页面的数量低于设定阈值时,页面清理线程会刷新脏页面。对于排序索引构建,脏页会立即刷新,以减少检查点开销并并行化 I/O 和 CPU 活动。

排序索引构建和优化器统计

排序索引构建可能会导致 优化器统计信息与以前的索引创建方法生成的统计信息不同。统计数据的差异预计不会影响工作负载性能,这是由于用于填充索引的算法不同所致。

MySQL 5.7 参考手册  /  ...  /  InnoDB 全文索引
14.6.2.4 InnoDB 全文索引

全文索引是在基于文本的列(CHAR、 VARCHAR或 TEXTcolumns)上创建的,以加快对这些列中包含的数据的查询和 DML 操作。

全文索引定义为语句的一部分 或使用 或CREATE TABLE添加到现有表中。 ALTER TABLECREATE INDEX

全文搜索是使用MATCH() ... AGAINST语法执行的。有关使用信息,请参阅 第 12.9 节 “全文搜索功能”。

InnoDB全文索引在本节的以下主题下进行描述:

  • InnoDB全文索引设计

  • InnoDB 全文索引表

  • InnoDB全文索引缓存

  • InnoDB 全文索引 DOC_ID 和 FTS_DOC_ID 列

  • InnoDB 全文索引删除处理

  • InnoDB 全文索引事务处理

  • 监控InnoDB全文索引

InnoDB全文索引设计

InnoDB全文索引采用倒排索引设计。倒排索引存储单词列表,对于每个单词,存储该单词出现的文档列表。为了支持邻近搜索,还存储每个单词的位置信息(作为字节偏移量)。

InnoDB 全文索引表

创建全文索引时InnoDB,会创建一组索引表,如下例所示:

mysql> CREATE TABLE opening_lines (
       id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
       opening_line TEXT(500),
       author VARCHAR(200),
       title VARCHAR(200),
       FULLTEXT idx (opening_line)
       ) ENGINE=InnoDB;

mysql> SELECT table_id, name, space from INFORMATION_SCHEMA.INNODB_SYS_TABLES
       WHERE name LIKE 'test/%';
+----------+----------------------------------------------------+-------+
| table_id | name                                               | space |
+----------+----------------------------------------------------+-------+
|      333 | test/FTS_0000000000000147_00000000000001c9_INDEX_1 |   289 |
|      334 | test/FTS_0000000000000147_00000000000001c9_INDEX_2 |   290 |
|      335 | test/FTS_0000000000000147_00000000000001c9_INDEX_3 |   291 |
|      336 | test/FTS_0000000000000147_00000000000001c9_INDEX_4 |   292 |
|      337 | test/FTS_0000000000000147_00000000000001c9_INDEX_5 |   293 |
|      338 | test/FTS_0000000000000147_00000000000001c9_INDEX_6 |   294 |
|      330 | test/FTS_0000000000000147_BEING_DELETED            |   286 |
|      331 | test/FTS_0000000000000147_BEING_DELETED_CACHE      |   287 |
|      332 | test/FTS_0000000000000147_CONFIG                   |   288 |
|      328 | test/FTS_0000000000000147_DELETED                  |   284 |
|      329 | test/FTS_0000000000000147_DELETED_CACHE            |   285 |
|      327 | test/opening_lines                                 |   283 |
+----------+----------------------------------------------------+-------+

前六个索引表构成倒排索引,称为辅助索引表。当传入文档被标记化时,各个单词(也称为 “标记”)将与位置信息和关联的 DOC_ID. 根据单词第一个字符的字符集排序权重,对单词进行完全排序并在六个索引表中进行分区。

倒排索引被划分为6个辅助索引表,以支持并行索引创建。默认情况下,两个线程对单词和关联数据进行标记、排序和插入到索引表中。执行此工作的线程数可以使用该 innodb_ft_sort_pll_degree 变量进行配置。在大型表上创建全文索引时,请考虑增加线程数。

辅助索引表名称前缀为 fts_,后缀为 。每个辅助索引表通过辅助索引表名称中与索引表的匹配的十六进制值与索引表关联 。例如,表的 是 ,其十六进制值为 0x147。如前面的示例所示,“ 147 ”十六进制值出现在与该表关联的辅助索引表的名称中。 index_#table_idtable_idtest/opening_lines327test/opening_lines

index_id表示全文索引的 十六进制值也出现在辅助索引表名称中。例如,在辅助表 name 中 test/FTS_0000000000000147_00000000000001c9_INDEX_1,十六进制值1c9的十进制值为 457。可以通过在信息模式表中查询 该值 (457) 来识别opening_lines表 ( ) 上定义的索引。 idxINNODB_SYS_INDEXES

mysql> SELECT index_id, name, table_id, space from INFORMATION_SCHEMA.INNODB_SYS_INDEXES
       WHERE index_id=457;
+----------+------+----------+-------+
| index_id | name | table_id | space |
+----------+------+----------+-------+
|      457 | idx  |      327 |   283 |
+----------+------+----------+-------+

如果主表是在每个 表文件的表 空间中创建的,则索引表存储在它们自己的表空间中。否则,索引表存储在索引表所在的表空间中。

上例中显示的其他索引表称为普通索引表,用于删除处理和存储全文索引的内部状态。与为每个全文索引创建的倒排索引表不同,这组表对于在特定表上创建的所有全文索引是通用的。

即使删除全文索引,公共索引表也会保留。当删除全文索引时,为该 FTS_DOC_ID索引创建的列将被保留,因为删除该FTS_DOC_ID 列将需要重建以前索引的表。需要公共索引表来管理 FTS_DOC_ID列。

  • FTS_*_DELETED和 FTS_*_DELETED_CACHE

    包含已删除但其数据尚未从全文索引中删除的文档的文档 ID (DOC_ID)。这FTS_*_DELETED_CACHE是表的内存版本FTS_*_DELETED 。

  • FTS_*_BEING_DELETED和 FTS_*_BEING_DELETED_CACHE

    包含已删除且其数据当前正在从全文索引中删除的文档的文档 ID (DOC_ID)。该 FTS_*_BEING_DELETED_CACHE表是表的内存版本 FTS_*_BEING_DELETED

  • FTS_*_CONFIG

    存储有关全文索引内部状态的信息。最重要的是,它存储 FTS_SYNCED_DOC_ID,它标识已解析并刷新到磁盘的文档。在崩溃恢复的情况下,FTS_SYNCED_DOC_ID 值用于识别尚未刷新到磁盘的文档,以便可以重新解析文档并将其添加回全文索引缓存。要查看此表中的数据,请查询信息架构 INNODB_FT_CONFIG表。

InnoDB全文索引缓存

插入文档时,它会被标记化,并且各个单词和关联数据将被插入到全文索引中。即使对于小文档,此过程也可能会导致对辅助索引表进行大量小插入,从而使对这些表的并发访问成为争论点。为了避免这个问题,InnoDB使用全文索引缓存来临时缓存最近插入的行的索引表插入。这种内存中的缓存结构会保存插入,直到缓存已满,然后将它们批量刷新到磁盘(辅助索引表)。您可以查询信息架构 INNODB_FT_INDEX_CACHE表以查看最近插入的行的标记化数据。

缓存和批量刷新行为避免了对辅助索引表的频繁更新,这可能会导致在繁忙的插入和更新期间出现并发访问问题。批处理技术还可以避免同一单词的多次插入,并最大限度地减少重复条目。不是单独刷新每个单词,而是将同一单词的插入合并并作为单个条目刷新到磁盘,从而提高插入效率,同时保持辅助索引表尽可能小。

该innodb_ft_cache_size 变量用于配置全文索引缓存大小(基于每个表),这会影响刷新全文索引缓存的频率。您还可以使用该变量为给定实例中的所有表定义全局全文索引缓存大小限制 innodb_ft_total_cache_size 。

全文索引缓存存储与辅助索引表相同的信息。但是,全文索引缓存仅缓存最近插入的行的标记化数据。查询时,已经刷新到磁盘(辅助索引表)的数据不会被带回到全文索引缓存中。直接查询辅助索引表中的数据,并将辅助索引表的结果与全文索引缓存的结果合并后返回。

InnoDB 全文索引 DOC_ID 和 FTS_DOC_ID 列

InnoDB使用称为 的唯一文档标识符将DOC_ID全文索引中的单词映射到该单词出现的文档记录。该映射需要FTS_DOC_ID索引表上的一列。如果FTS_DOC_ID未定义列,则在创建全文索引时InnoDB自动添加隐藏 列。FTS_DOC_ID以下示例演示了此行为。

下表定义不包含 FTS_DOC_ID列:

mysql> CREATE TABLE opening_lines (
       id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
       opening_line TEXT(500),
       author VARCHAR(200),
       title VARCHAR(200)
       ) ENGINE=InnoDB;

当您使用语法在表上创建全文索引时 CREATE FULLTEXT INDEX,会返回一条警告,报告InnoDB正在重建表以添加列FTS_DOC_ID 。

mysql> CREATE FULLTEXT INDEX idx ON opening_lines(opening_line);
Query OK, 0 rows affected, 1 warning (0.19 sec)
Records: 0  Duplicates: 0  Warnings: 1

mysql> SHOW WARNINGS;
+---------+------+--------------------------------------------------+
| Level   | Code | Message                                          |
+---------+------+--------------------------------------------------+
| Warning |  124 | InnoDB rebuilding table to add column FTS_DOC_ID |
+---------+------+--------------------------------------------------+

ALTER TABLE当使用向没有列的表添加全文索引 时,会返回相同的警告 FTS_DOC_ID。如果您在创建全文索引时CREATE TABLE未指定FTS_DOC_ID列, InnoDB则会添加隐藏 FTS_DOC_ID列,且不会发出警告。

与在已加载数据的表上创建全文索引相比,一次性 定义FTS_DOC_ID列 的成本要低。CREATE TABLE如果FTS_DOC_ID 在加载数据之前在表上定义了列,则不必重建表及其索引来添加新列。如果您不关心CREATE FULLTEXT INDEX性能,请忽略该 FTS_DOC_ID列,让 InnoDB我们为您创建它。 InnoDB创建一个隐藏 FTS_DOC_ID列以及FTS_DOC_ID_INDEX该列上的唯一索引 ( ) FTS_DOC_ID。如果您想创建自己的FTS_DOC_ID列,则该列必须定义为BIGINT UNSIGNED NOT NULL并命名 FTS_DOC_ID(全部大写),如下例所示:

笔记

FTS_DOC_ID列不需要定义为AUTO_INCREMENT列,但这样做可以使加载数据更容易。

mysql> CREATE TABLE opening_lines (
       FTS_DOC_ID BIGINT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
       opening_line TEXT(500),
       author VARCHAR(200),
       title VARCHAR(200)
       ) ENGINE=InnoDB;

如果您选择自己定义FTS_DOC_ID列,则您负责管理该列以避免空值或重复值。FTS_DOC_ID值不能重复使用,这意味着FTS_DOC_ID 值必须不断增加。

或者,您可以在列上创建所需的唯一值 FTS_DOC_ID_INDEX(全部大写) FTS_DOC_ID

mysql> CREATE UNIQUE INDEX FTS_DOC_ID_INDEX on opening_lines(FTS_DOC_ID);

如果您不创建FTS_DOC_ID_INDEX, InnoDB则会自动创建。

在 MySQL 5.7.13 之前,最大已用 FTS_DOC_ID值与新 FTS_DOC_ID值之间允许的差距为 10000。在 MySQL 5.7.13 及更高版本中,允许的差距为 65535。

为了避免重建表,FTS_DOC_ID 删除全文索引时会保留该列。

InnoDB 全文索引删除处理

删除具有全文索引列的记录可能会导致辅助索引表中出现大量小删除,从而使对这些表的并发访问成为争论点。为了避免这个问题,每当DOC_ID从索引表中删除一条记录 FTS_*_DELETED时,已删除文档的记录就会记录在一个特殊的表中,而索引记录仍保留在全文索引中。在返回查询结果之前,FTS_*_DELETED会利用表中的信息过滤掉已删除的内容DOC_IDs。这种设计的好处是删除速度快且成本低。缺点是删除记录后索引的大小不会立即减小。要删除已删除记录的全文索引条目,请OPTIMIZE TABLE在索引表上 运行innodb_optimize_fulltext_only=ON 来重建全文索引。有关更多信息,请参阅 优化 InnoDB 全文索引。

InnoDB 全文索引事务处理

InnoDB由于其缓存和批处理行为,全文索引具有特殊的事务处理特性。具体来说,全文索引的更新和插入是在事务提交时处理的,这意味着全文搜索只能看到已提交的数据。以下示例演示了此行为。全文搜索仅在提交插入的行后返回结果。

mysql> CREATE TABLE opening_lines (
       id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
       opening_line TEXT(500),
       author VARCHAR(200),
       title VARCHAR(200),
       FULLTEXT idx (opening_line)
       ) ENGINE=InnoDB;

mysql> BEGIN;

mysql> INSERT INTO opening_lines(opening_line,author,title) VALUES
       ('Call me Ishmael.','Herman Melville','Moby-Dick'),
       ('A screaming comes across the sky.','Thomas Pynchon','Gravity\'s Rainbow'),
       ('I am an invisible man.','Ralph Ellison','Invisible Man'),
       ('Where now? Who now? When now?','Samuel Beckett','The Unnamable'),
       ('It was love at first sight.','Joseph Heller','Catch-22'),
       ('All this happened, more or less.','Kurt Vonnegut','Slaughterhouse-Five'),
       ('Mrs. Dalloway said she would buy the flowers herself.','Virginia Woolf','Mrs. Dalloway'),
       ('It was a pleasure to burn.','Ray Bradbury','Fahrenheit 451');

mysql> SELECT COUNT(*) FROM opening_lines WHERE MATCH(opening_line) AGAINST('Ishmael');
+----------+
| COUNT(*) |
+----------+
|        0 |
+----------+

mysql> COMMIT;

mysql> SELECT COUNT(*) FROM opening_lines WHERE MATCH(opening_line) AGAINST('Ishmael');
+----------+
| COUNT(*) |
+----------+
|        1 |
+----------+
监控InnoDB全文索引

InnoDB您可以通过查询下表来监视和检查全文索引 的特殊文本处理方面INFORMATION_SCHEMA

  • INNODB_FT_CONFIG

  • INNODB_FT_INDEX_TABLE

  • INNODB_FT_INDEX_CACHE

  • INNODB_FT_DEFAULT_STOPWORD

  • INNODB_FT_DELETED

  • INNODB_FT_BEING_DELETED

INNODB_SYS_INDEXES您还可以通过查询和 来查看全文索引和表的基本信息 INNODB_SYS_TABLES。

有关更多信息,请参阅 第 14.16.4 节,“InnoDB INFORMATION_SCHEMA FULLTEXT 索引表”。

你可能感兴趣的:(mysql,数据库,运维)