全文索引是在基于文本的列(CHAR、VARCHAR 或 TEXT 列)上创建的,以加快对这些列中包含数据的查询和 DML 操作。
全文索引在 CREATE TABLE 语句中定义,或使用 ALTER TABLE 或 CREATE INDEX 添加到现有表中。
全文搜索使用 MATCH() ... AGAINST 语法执行。有关用法信息,请参见 第 14.9 节,“全文搜索函数”。
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 |
+----------+----------------------------------------------------+-------+前六个索引表构成倒排索引,称为辅助索引表。当对传入的文档进行分词时,单个单词(也称为 “标记”)以及位置信息和关联的 DOC_ID 会被插入到索引表中。这些单词会根据单词第一个字符的字符集排序权重进行完全排序和分区,并分配到六个索引表中。
倒排索引被分成六个辅助索引表,以支持并行索引创建。默认情况下,两个线程对单词进行分词、排序,并将单词和关联数据插入索引表中。执行此操作的线程数可以使用 innodb_ft_sort_pll_degree 变量配置。在大型表上创建全文索引时,请考虑增加线程数。
辅助索引表名称以 fts_ 为前缀,以 index_ 为后缀。每个辅助索引表都通过辅助索引表名称中的十六进制值与索引表相关联,该值与索引表的 #table_id 相匹配。例如,test/opening_lines 表的 table_id 为 327,其十六进制值为 0x147。如前例所示,十六进制值 “147” 出现在与 test/opening_lines 表关联的辅助索引表名称中。
表示全文索引的 index_id 的十六进制值也出现在辅助索引表名称中。例如,在辅助表名称 test/fts_0000000000000147_00000000000001c9_index_1 中,十六进制值 1c9 的十进制值为 457。在 opening_lines 表(idx)上定义的索引可以通过查询信息模式 INNODB_INDEXES 表获取此值 (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_*_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值用于标识尚未刷新到磁盘的文档,以便可以重新解析这些文档并将它们添加回全文索引缓存。要查看此表中的数据,请查询 Information SchemaINNODB_FT_CONFIG表。
当插入文档时,它将被分词,并且各个单词和相关数据将插入全文索引。即使对于小型文档,此过程也会导致在辅助索引表中进行大量的小型插入操作,这使得并发访问这些表成为一个争用点。为了避免此问题,InnoDB 使用全文索引缓存来临时缓存最近插入行的索引表插入操作。这种内存缓存结构将插入操作保留在其中,直到缓存已满,然后将它们批量刷新到磁盘(到辅助索引表)。您可以查询 Information Schema INNODB_FT_INDEX_CACHE 表以查看最近插入行的分词数据。
缓存和批量刷新行为避免了对辅助索引表的频繁更新,这可能会导致在繁忙的插入和更新时间段内出现并发访问问题。批量技术还避免了对同一单词的多次插入,并将重复条目降至最低。插入操作不是分别刷新每个单词,而是将同一单词的插入操作合并并作为单个条目刷新到磁盘,从而提高插入效率,同时将辅助索引表的大小保持在尽可能小。
innodb_ft_cache_size 变量用于配置全文索引缓存大小(按表为单位),这会影响全文索引缓存的刷新频率。您还可以使用 innodb_ft_total_cache_size 变量为给定实例中的所有表定义全局全文索引缓存大小限制。
全文索引缓存存储与辅助索引表相同的信息。但是,全文索引缓存仅缓存最近插入行的分词数据。已刷新到磁盘(到辅助索引表)的数据在查询时不会被带回全文索引缓存。直接查询辅助索引表中的数据,并将辅助索引表中的结果与全文索引缓存中的结果合并,然后再返回。
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 列上的唯一索引 (FTS_DOC_ID_INDEX)。如果您想创建自己的 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 列上创建所需的唯一 FTS_DOC_ID_INDEX(全部大写)。
mysql> CREATE UNIQUE INDEX FTS_DOC_ID_INDEX on opening_lines(FTS_DOC_ID);如果您没有创建 FTS_DOC_ID_INDEX,InnoDB 将自动创建它。
FTS_DOC_ID_INDEX 不能定义为降序索引,因为 InnoDB SQL 解析器不使用降序索引。
最大使用的 FTS_DOC_ID 值与新的 FTS_DOC_ID 值之间允许的间隙为 65535。
为了避免重建表,在删除全文索引时会保留 FTS_DOC_ID 列。
删除具有全文索引列的记录会导致辅助索引表中出现大量的小型删除操作,这使得并发访问这些表成为一个争用点。为了避免此问题,每当从已索引的表中删除记录时,已删除文档的 DOC_ID 将被记录在特殊的 FTS_*_DELETED 表中,而已索引的记录将保留在全文索引中。在返回查询结果之前,FTS_*_DELETED 表中的信息将用于过滤掉已删除的 DOC_ID。这种设计的优点是删除操作速度快且成本低廉。缺点是索引的大小不会在删除记录后立即减小。要删除已删除记录的全文索引条目,请对已索引的表运行 OPTIMIZE TABLE,并使用 innodb_optimize_fulltext_only=ON 来重建全文索引。有关更多信息,请参见 优化 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 |
+----------+
您可以通过查询以下 INFORMATION_SCHEMA 表来监控和检查 InnoDB 全文索引的特殊文本处理方面
您还可以通过查询 INNODB_INDEXES 和 INNODB_TABLES 来查看全文索引和表的详细信息。
有关更多信息,请参见 第 17.15.4 节,“InnoDB INFORMATION_SCHEMA 全文索引表”。