Hash索引
主要就是通過Hash算法(常見的Hash算法有直接定址法、平方取中法、折疊法、除數取余法、隨機數法),將數據庫字段數據轉換成定長的Hash值,與這條數據的行指針一並存入Hash表的對應位置;如果發生Hash碰撞(兩個不同關鍵字的Hash值相同),則在對應Hash鍵下以鏈表形式存儲。
檢索算法:在檢索查詢時,就再次對待查關鍵字再次執行相同的Hash算法,得到Hash值,到對應Hash表對應位置取出數據即可,如果發生Hash碰撞,則需要在取值時進行篩選。目前使用Hash索引的數據庫並不多,主要有Memory等。
MySQL目前有Memory引擎和NDB引擎支持Hash索引。
full-text全文索引
全文索引也是MyISAM的一種特殊索引類型,主要用於全文索引,InnoDB從MYSQL5.6版本提供對全文索引的支持。
它用於替代效率較低的LIKE模糊匹配操作,而且可以通過多字段組合的全文索引一次性全模糊匹配多個字段。
同樣使用B-Tree存放索引數據,但使用的是特定的算法,將字段數據分割后再進行索引(一般每4個字節一次分割),索引文件存儲的是分割前的索引字符串集合,與分割后的索引信息,對應Btree結構的節點存儲的是分割后的詞信息以及它在分割前的索引字符串集合中的位置
概念
通過數值比較、范圍過濾等就可以完成絕大多數我們需要的查詢,但是,如果希望通過關鍵字的匹配來進行查詢過濾,那么就需要基於相似度的查詢,而不是原來的精確數值比較。全文索引就是為這種場景設計的。
你可能會說,用 like + % 就可以實現模糊匹配了,為什么還要全文索引?like + % 在文本比較少時是合適的,但是對於大量的文本數據檢索,是不可想象的。全文索引在大量的數據面前,能比 like + % 快 N 倍,速度不是一個數量級,但是全文索引可能存在精度問題。
你可能沒有注意過全文索引,不過至少應該對一種全文索引技術比較熟悉:各種的搜索引擎。雖然搜索引擎的索引對象是超大量的數據,並且通常其背后都不是關系型數據庫,不過全文索引的基本原理是一樣的。
版本支持
開始之前,先說一下全文索引的版本、存儲引擎、數據類型的支持情況
- MySQL 5.6 以前的版本,只有 MyISAM 存儲引擎支持全文索引;
- MySQL 5.6 及以后的版本,MyISAM 和 InnoDB 存儲引擎均支持全文索引;
- 只有字段的數據類型為 char、varchar、text 及其系列才可以建全文索引。
測試或使用全文索引時,要先看一下自己的 MySQL 版本、存儲引擎和數據類型是否支持全文索引。
操作全文索引
索引的操作隨便一搜都是,這里還是再啰嗦一遍。
創建
1.創建表時創建全文索引
create table fulltext_test ( id int(11) NOT NULL AUTO_INCREMENT, content text NOT NULL, tag varchar(255), PRIMARY KEY (id), FULLTEXT KEY content_tag_fulltext(content,tag) // 創建聯合全文索引列
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
2.在已存在的表上創建全文索引
create fulltext index content_tag_fulltext
on fulltext_test(content,tag);
3.通過 SQL 語句 ALTER TABLE 創建全文索引
alter table fulltext_test
add fulltext index content_tag_fulltext(content,tag);
刪除
1.直接使用 DROP INDEX 刪除全文索引
drop index content_tag_fulltext
on fulltext_test;
2.通過 SQL 語句 ALTER TABLE 刪除全文索引
alter table fulltext_test
drop index content_tag_fulltext;
使用全文索引
和常用的模糊匹配使用 like + % 不同,全文索引有自己的語法格式,使用 match 和 against 關鍵字,比如
select * from fulltext_test where match(content,tag) against('xxx xxx');
注意: match() 函數中指定的列必須和全文索引中指定的列完全相同,否則就會報錯,無法使用全文索引,這是因為全文索引不會記錄關鍵字來自哪一列。如果想要對某一列使用全文索引,請單獨為該列創建全文索引。
測試全文索引
添加測試數據
有了上面的知識,就可以測試一下全文索引了。
首先創建測試表,插入測試數據
create table test (
id int(11) unsigned not null auto_increment,
content text not null,
primary key(id),
fulltext key content_index(content)
) engine=MyISAM 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');
根據我們的慣性思維,應該會顯示 4 條記錄才對,然而結果是 1 條記錄也沒有,只有在執行下面的查詢時
select * from test where match(content) against('aaaa');
才會搜到 aaaa 這 1 條記錄。
為什么?這個問題有很多原因,其中最常見的就是 最小搜索長度 導致的。另外插一句,使用全文索引時,測試表里至少要有 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;
可以看到最小搜索長度 MyISAM 引擎下默認是 4,InnoDB 引擎下是 3,也即,MySQL 的全文索引只會對長度大於等於 4 或者 3 的詞語建立索引,而剛剛搜索的只有 aaaa 的長度大於等於 4。
兩種全文索引
自然語言的全文索引
默認情況下,或者使用 in natural language mode 修飾符時,match() 函數對文本集合執行自然語言搜索,上面的例子都是自然語言的全文索引。
自然語言搜索引擎將計算每一個文檔對象和查詢的相關度。這里,相關度是基於匹配的關鍵詞的個數,以及關鍵詞在文檔中出現的次數。在整個索引中出現次數越少的詞語,匹配時的相關度就越高。相反,非常常見的單詞將不會被搜索,如果一個詞語的在超過 50% 的記錄中都出現了,那么自然語言的搜索將不會搜索這類詞語。上面提到的,測試表中必須有 4 條以上的記錄,就是這個原因。
這個機制也比較好理解,比如說,一個數據表存儲的是一篇篇的文章,文章中的常見詞、語氣詞等等,出現的肯定比較多,搜索這些詞語就沒什么意義了,需要搜索的是那些文章中有特殊意義的詞,這樣才能把文章區分開。
布爾全文索引
在布爾搜索中,我們可以在查詢中自定義某個被搜索的詞語的相關性,當編寫一個布爾搜索查詢時,可以通過一些前綴修飾符來定制搜索。
MySQL 內置的修飾符,上面查詢最小搜索長度時,搜索結果 ft_boolean_syntax 變量的值就是內置的修飾符,下面簡單解釋幾個,更多修飾符的作用可以查手冊
- + 必須包含該詞
- - 必須不包含該詞
- > 提高該詞的相關性,查詢的結果靠前
- < 降低該詞的相關性,查詢的結果靠后
- (*)星號 通配符,只能接在詞后面
對於上面提到的問題,可以使用布爾全文索引查詢來解決,使用下面的命令,a、aa、aaa、aaaa 就都被查詢出來了。
select * test where match(content) against('a*' in boolean mode);
總結
好了,差不多寫完了,又到了總結的時候。
MySQL 的全文索引最開始僅支持英語,因為英語的詞與詞之間有空格,使用空格作為分詞的分隔符是很方便的。亞洲文字,比如漢語、日語、漢語等,是沒有空格的,這就造成了一定的限制。不過 MySQL 5.7.6 開始,引入了一個 ngram 全文分析器來解決這個問題,並且對 MyISAM 和 InnoDB 引擎都有效。
事實上,MyISAM 存儲引擎對全文索引的支持有很多的限制,例如表級別鎖對性能的影響、數據文件的崩潰、崩潰后的恢復等,這使得 MyISAM 的全文索引對於很多的應用場景並不適合。所以,多數情況下的建議是使用別的解決方案,例如 Sphinx、Lucene 等等第三方的插件,亦或是使用 InnoDB 存儲引擎的全文索引。
幾個注意點
- 使用全文索引前,搞清楚版本支持情況;
- 全文索引比 like + % 快 N 倍,但是可能存在精度問題;
- 如果需要全文索引的是大量數據,建議先添加數據,再創建索引;
- 對於中文,可以使用 MySQL 5.7.6 之后的版本,或者第三方插件。
