MySQL索引系列:全文索引

0Robert0發表於2020-11-07

什麼是全文索引?

全文索引首先是 MySQL 的一種索引型別,也是搜尋引擎的關鍵技術。

試想在1M大小的檔案中搜尋一個詞,可能需要幾秒,在100M的檔案中可能需要幾十秒,如果在更大的檔案中搜尋那麼就需要更大的系統開銷,這樣的開銷是不現實的。

所以在這樣的矛盾下出現了全文索引技術,有時候有人叫倒排文件技術。

全文索引的作用是什麼?

全文索引是將儲存在資料庫中的大段文字中的任意內容資訊查詢出來的技術。

既然是查詢包含某些內容的文字,用 like + 萬用字元 或者正規表示式就可以實現模糊匹配,為什麼還要全文索引?

  • 效能:萬用字元和正規表示式匹配通常要求MySQL嘗試匹配表中所有行(而且這些搜尋極少使用表索引)。因此,由於被搜尋行數不斷增加,這些搜尋可能非常耗時。
  • 明確控制:使用萬用字元和正規表示式匹配,很難明確地控制匹配什麼和不匹配什麼。例如,指定一個詞必須匹配,一個詞必須不匹配;而一個詞僅在第一個詞確實匹配的情況下,才可以匹配或者才可以不匹配等。這些情況,使用萬用字元和正規表示式都不滿足。
  • 智慧化的結果:雖然基於萬用字元和正規表示式的搜尋提供了非常靈活的搜尋方式,但它們都不能提供一種智慧化的選擇結果的方法。 例如,一個特殊詞的搜尋將會返回包含該詞的所有行,而不區分包含單個匹配的行和包含多個匹配的行(按照可能是更好的匹配來排列它們)。類似,一個特殊詞的搜尋將不會找出不包含該詞但 包含其他相關詞的行。

所有這些限制以及更多的限制都可以用全文字搜尋來解決。在使用全文字搜尋時,MySQL不需要分別檢視每個行,不需要分別分析和處理每個詞,可以根據需要獲取全文中有關章,節,段,句,詞等資訊,也可以進行各種統計和分析。MySQL建立指定列中各詞的一個索引,搜尋可以針對這些詞進行。這樣,MySQL可以快速有效地決定哪些詞匹配(哪些行包含它們), 哪些詞不匹配,它們匹配的頻率,等等。

但是全文索引可能存在精度問題

假如我們要搜尋 胡歌很帥,拿百度來舉個例子:
在這裡插入圖片描述

可以看到我明明搜尋的是 ‘胡歌很帥’,但是百度搜尋的關鍵字(標紅的就是關鍵字)卻不只是完整的 ‘胡歌很帥’,這一句話被分割為’胡歌’,‘很帥’,‘帥’,‘胡歌很’,'胡歌很帥’等,這就是全文索引裡的分詞機制,也是導致精度問題的原因。

版本支援

  1. MySQL 5.6 以前的版本,只有 MyISAM 儲存引擎支援全文索引,InnoDB儲存引擎並不支援全文索引技術,大多數的使用者轉向MyISAM儲存引擎,雖然可以通過表的拆分,將進行全文索引的資料儲存為MyIsam表,這樣方式解決邏輯業務的需求,但是卻喪失了INNODB儲存引擎的事務性;
  2. MySQL 5.6 及以後的版本,MyISAM 和 InnoDB 儲存引擎均支援全文索引;
  3. 只有欄位的資料型別為 char、varchar、text 及其系列才可以建全文索引。

索引的建立、修改、刪除

具體操作就不重複了,請看上一篇部落格:MySQL索引系列:索引概述

使用全文索引

首先建立表,插入測試資料

create table test (
    id int(11) unsigned not null auto_increment,
    content text not null,
    primary key(id),
    fulltext key content_index(content)
) engine=Innodb default charset=utf8;

insert into test (content) values ('a'),('b'),('c');
insert into test (content) values ('aa'),('bb'),('cc');
insert into test (content) values ('aaa'),('bbb'),('ccc');
insert into test (content) values ('aaaa'),('bbbb'),('cccc');

按照全文索引的使用語法執行下面查詢:

select * from test where match(content) against('a');
select * from test where match(content) against('aa');
select * from test where match(content) against('aaa');

# 使用完整的 Match() 說明傳遞給 Match() 的值必須與 FULLTEXT() 定義中的相同。如果指定多個列,則必須列
# 出它們(而且次序正確)。且搜尋不區分大小寫。

結果發現只有最後那條SQL有一條記錄,為什麼呢?

這個問題有很多原因,其中最常見的就是 最小搜尋長度 導致的。另外插一句,使用全文索引時,測試表裡至少要有 4 條以上的記錄,否則,會出現意想不到的結果。

MySQL 中的全文索引,有兩個變數,最小搜尋長度和最大搜尋長度,對於長度小於最小搜尋長度和大於最大搜尋長度的詞語,都不會被索引。通俗點就是說,想對一個詞語使用全文索引搜尋,那麼這個詞語的長度必須在以上兩個變數的區間內。

這兩個的預設值可以使用以下命令檢視

show variables like '%ft%';

可以看到這兩個變數在 MyISAM 和 InnoDB 兩種儲存引擎下的變數名和預設值

// MyISAM
ft_min_word_len = 4;
ft_max_word_len = 84;

// InnoDB
innodb_ft_min_token_size = 3;
innodb_ft_max_token_size = 84;1234567

可以看到最小搜尋長度 MyISAM 引擎下預設是 4,InnoDB 引擎下是 3,也即,MySQL 的全文索引只會對長度大於等於 4 或者 3 的詞語建立索引,而剛剛搜尋的只有 rabbit 的長度大於等於 3。

配置最小搜尋長度

全文索引的相關引數都無法進行動態修改,必須通過修改 MySQL 的配置檔案來完成。修改最小搜尋長度的值為 1,首先開啟 MySQL 的配置檔案 /etc/my.cnf,在 [mysqld] 的下面追加以下內容

[mysqld]
innodb_ft_min_token_size = 1

然後重啟 MySQL 伺服器,並修復全文索引。注意,修改完引數以後,一定要修復下索引,不然引數不會生效。

兩種修復方式,可以使用下面的命令修復

repair table productnotes quick;

或者直接刪掉重新建立索引,再次執行上面的查詢,就都可以查出來了。

全文搜尋的分類

  • 自然語言的全文搜尋

  • 布林全文搜尋

  • 帶查詢擴充套件的全文搜尋

關於這幾個分類,具體描述還是看官方手冊手冊吧

參考文章
官方手冊

MySQL必知必會
全文索引的原理

相關文章