全文索引建立在文字型資料行 (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。藉由查詢資訊綱要 INNODB_INDEXES
表格 (數值為 457),即可識別在 opening_lines
表格上定義的索引 (idx
)。
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 |
+----------+------+----------+-------+
如果主資料表是在單表空間 (file-per-table) 表空間中建立,則索引表會儲存在其自己的表空間中。否則,索引表會儲存在被索引資料表所在的表空間中。
前面範例中顯示的其他索引表被稱為共用索引表,用於處理刪除操作和儲存全文索引的內部狀態。與為每個全文索引建立的反向索引表不同,這組表對於在特定資料表上建立的所有全文索引都是通用的。
即使刪除全文索引,共用索引表也會保留。當刪除全文索引時,為該索引建立的 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 Schema 的INNODB_FT_CONFIG
資料表。
當插入文件時,會進行 Token 化,並且將個別的字詞和相關資料插入到全文索引中。即使是小型文件,這個過程也可能導致對輔助索引表進行多次小型插入,從而使對這些表的並行存取成為一個競爭點。為了避免這個問題,InnoDB
使用全文索引快取來臨時快取最近插入的列的索引表插入。這個記憶體快取結構會保留插入,直到快取滿了,然後將它們批次刷新到磁碟 (輔助索引表)。您可以查詢 Information Schema 的 INNODB_FT_INDEX_CACHE
資料表,以檢視最近插入的列的 Token 化資料。
快取和批次刷新行為避免了對輔助索引表的頻繁更新,這可能會在繁忙的插入和更新期間導致並行存取問題。批次處理技術還可以避免同一個字詞的多個插入,並將重複條目降至最低。不是單獨刷新每個字詞,而是合併同一個字詞的插入,並作為單一條目刷新到磁碟,從而提高插入效率,同時使輔助索引表盡可能小。
innodb_ft_cache_size
變數用於設定全文索引快取大小 (每個資料表),這會影響全文索引快取的刷新頻率。您也可以使用 innodb_ft_total_cache_size
變數,為給定執行個體中的所有資料表定義全域全文索引快取大小限制。
全文索引快取儲存與輔助索引表相同的資訊。但是,全文索引快取只快取最近插入的列的 Token 化資料。當查詢時,已刷新到磁碟(輔助索引表)的資料不會帶回到全文索引快取中。輔助索引表中的資料會被直接查詢,並且在傳回結果之前,輔助索引表的結果會與全文索引快取的結果合併。
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
。這種設計的好處是刪除操作快速且成本低廉。缺點是在刪除記錄後,索引的大小不會立即縮小。若要刪除已刪除記錄的全文索引項目,請在索引資料表上使用 innodb_optimize_fulltext_only=ON
執行 OPTIMIZE TABLE
,以重建全文索引。如需更多資訊,請參閱最佳化 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 FULLTEXT 索引表」。