全文索引是在文字型欄位(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
)可以透過查詢 Information Schema 的 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 |
+----------+------+----------+-------+
如果主要資料表是在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
表。
當插入文件時,會將其斷詞,並將個別文字和相關資料插入全文索引中。即使是小型文件,此過程也可能會導致多次小型插入輔助索引表,從而使對這些表的並行存取成為爭用點。為避免此問題,InnoDB
使用全文索引快取來暫時快取最近插入的列的索引表插入。此記憶體快取結構會保存插入,直到快取已滿,然後將它們批量刷新到磁碟(到輔助索引表)。您可以查詢 Information Schema 的 INNODB_FT_INDEX_CACHE
表,以檢視最近插入列的斷詞資料。
快取和批量刷新行為避免了對輔助索引表的頻繁更新,這可能會在忙碌的插入和更新期間導致並行存取問題。批次處理技術還避免了對相同文字的多個插入,並最大限度地減少了重複的條目。不是單獨刷新每個文字,而是合併對相同文字的插入,並作為單個條目刷新到磁碟,從而提高插入效率,同時使輔助索引表盡可能小。
innodb_ft_cache_size
變數用於設定全文索引快取大小(以每個資料表為基礎),這會影響全文索引快取的刷新頻率。您也可以使用 innodb_ft_total_cache_size
變數,為給定執行個體中的所有資料表定義全域全文索引快取大小限制。
全文索引快取會儲存與輔助索引表相同的資訊。但是,全文索引快取僅快取最近插入列的斷詞資料。當查詢時,已經刷新到磁碟(到輔助索引表)的資料不會被帶回全文索引快取中。直接查詢輔助索引表中的資料,並在傳回之前,將輔助索引表的結果與全文索引快取的結果合併。
InnoDB
使用稱為 DOC_ID
的唯一文件識別碼,將全文索引中的文字對應到文字出現的文件記錄。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
來檢視全文索引和資料表的基本資訊。