Documentation Home
MySQL 8.3 Reference Manual
Related Documentation Download this Manual
PDF (US Ltr) - 40.8Mb
PDF (A4) - 40.9Mb
Man Pages (TGZ) - 294.0Kb
Man Pages (Zip) - 409.0Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb
Excerpts from this Manual

MySQL 8.3 Reference Manual  /  ...  /  InnoDB Full-Text Indexes

17.6.2.4 InnoDB 全文索引

全文索引是在基于文本的列(CHARVARCHARTEXT 列)上创建的,以加速查询和 DML 操作在这些列中的数据。

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

全文搜索使用 MATCH() ... AGAINST 语法。有关使用信息,请参阅 第 14.9 节,“全文搜索函数”

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_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 |
+----------+----------------------------------------------------+-------+

前六个索引表组成了倒排索引,也称为辅助索引表。当传入文档被标记化时,单词(也称为“tokens”)将被插入索引表中,连同位置信息和关联的 DOC_ID。单词被完全排序并根据单词的第一个字符的字符集排序权重分区到六个索引表中。

倒排索引被分区到六个辅助索引表中,以支持并行索引创建。默认情况下,两个线程对传入文档进行标记化、排序和插入索引表中。可以使用 innodb_ft_sort_pll_degree 变量配置线程数。在创建大表的全文索引时,考虑增加线程数。

辅助索引表的名称以 fts_ 开头,以 index_# 结尾。每个辅助索引表都与索引表关联,通过表的 table_id 在辅助索引表名称中的十六进制值来关联。例如,test/opening_lines 表的 table_id327,十六进制值为 0x147。如前面的示例所示,十六进制值 147 出现在与 test/opening_lines 表关联的辅助索引表名称中。

十六进制值表示全文索引的 index_id 也出现在辅助索引表名中。例如,在辅助表名 test/fts_0000000000000147_00000000000001c9_index_1 中,十六进制值 1c9 的十进制值为 457。可以通过查询 Information Schema INNODB_INDEXES 表来标识在 opening_lines 表 (idx) 上定义的索引(457)。

mysql> SELECT index_id, name, table_id, space from INFORMATION_SCHEMA.INNODB_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_*_deletedfts_*_deleted_cache

    包含已删除但数据尚未从全文索引中删除的文档 ID(DOC_ID)。 fts_*_deleted_cachefts_*_deleted 表的内存版本。

  • fts_*_being_deletedfts_*_being_deleted_cache

    包含正在删除的文档 ID(DOC_ID),其数据当前正在从全文索引中删除。 fts_*_being_deleted_cache 表是 fts_*_being_deleted 表的内存版本。

  • fts_*_config

    存储全文索引的内部状态信息。最重要的是,它存储了 FTS_SYNCED_DOC_ID,该值标识了已解析和刷新到磁盘的文档。在崩溃恢复期间,FTS_SYNCED_DOC_ID 值用于标识未刷新到磁盘的文档,以便重新解析并将其添加回全文索引缓存中。要查看该表中的数据,请查询 Information Schema INNODB_FT_CONFIG 表。

InnoDB 全文索引缓存

当文档被插入时,它将被标记化,并将单个词和相关数据插入到全文索引中。这个过程,即使对于小文档,也可能会导致许多小的插入到辅助索引表中,从而使得这些表的并发访问成为一个点 contention。InnoDB 使用全文索引缓存来临时缓存索引表插入,以避免这个问题。这个内存缓存结构将插入缓存,直到缓存满,然后批量将其刷新到磁盘(到辅助索引表)。您可以查询 Information Schema INNODB_FT_INDEX_CACHE 表以查看最近插入的行的标记化数据。

缓存和批量刷新行为避免了频繁更新辅助索引表,这可能会在繁忙的插入和更新期间导致并发访问问题。批处理技术还避免了相同单词的多次插入,并将插入合并为单个条目,从而提高插入效率并将辅助索引表保持尽可能小。

变量 innodb_ft_cache_size 用于配置全文索引缓存大小(每个表的基础上),这将影响全文索引缓存何时刷新。您还可以使用 innodb_ft_total_cache_size 变量定义全文索引缓存的全局大小限制,以便在给定的实例中应用于所有表。

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

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 列,不会警告。

CREATE TABLE 时定义 FTS_DOC_ID 列比创建全文索引时更便宜。如果在加载数据之前定义了 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(所有大写),如下所示:

Note

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_INDEXInnoDB 将自动创建它。

Note

FTS_DOC_ID_INDEX 不能定义为降序索引,因为 InnoDB SQL 解析器不使用降序索引。

允许的最大 FTS_DOC_ID 值和新 FTS_DOC_ID 值之间的间隙为 65535。

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

InnoDB 全文索引删除处理

删除带有全文索引列的记录可能会在辅助索引表中导致许多小删除,从而使得这些表的并发访问成为一个争用点。为了避免这个问题,每当从索引表中删除记录时,删除的文档的 DOC_ID 都将被记录在特殊的 FTS_*_DELETED 表中,而索引记录仍保留在全文索引中。在返回查询结果之前,使用 FTS_*_DELETED 表中的信息来过滤掉已删除的 DOC_ID。这种设计的优点是删除操作非常快捷和廉价。缺点是索引的大小不会立即减少删除记录。要删除已删除记录的全文索引条目,请在索引表上运行 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 全文索引监控

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

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

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