MySQL技術內幕讀書筆記(六)——索引與算法之全文索引

全文索引

概述

​ 經過索引字段的前綴進行查找,B+樹索引是支持的,利用B+樹索引就能夠進行快速查詢。mysql

SELECT * FROM blog WHERE content like 'xxx%';

​ 可是查詢包含單詞的狀況,就無能爲力了。因此須要進入全文檢索技術Full-Test Searchsql

select * from blog where content like '%xxx%';

​ 全文檢索是將存儲於數據庫中的整本書或者整片文章中的任意內容信息查找出來的技術。數據庫

倒排索引

​ 全文檢索一般使用倒排索引inverted index來實現。倒排索引同B+樹索引同樣,也是一種索引結構。它在輔助表auxiliary table中存儲了單詞與單詞自身在一個或多個文檔中所在位置之間的映射。這一般利用關聯數組實現,其擁有兩種表現形式:數組

  • invert file index:{單詞,單詞所在文檔的ID}
  • full inverted index:{單詞,(單詞所在文檔的ID,在具體文檔中的位置)}

一個demo:緩存

DocumentId表示進行全文檢索文檔的ID,Text保溼存儲的內容,須要對內容進行全文檢索,獲得某個單詞的出現過的文檔ID。架構

​ 對於使用invert file index的關聯數組,其存儲內容以下,以後進行查找就簡單了併發

​ 對於使用full inverted index的關聯數組,其存儲內容以下ide

​ 不只存儲了ID,還存儲了出現的位置。空間佔用更多,可是功能更強。函數

InnoDB全文檢索

InnoDB存儲引擎採用full inverted index的方式,將(DocumentIdPosition)視爲一個ilist,所以在全文檢索表中,有兩個列,一個是word字段,另外一個是ilist字段。性能

InnoDB存儲引擎中,爲了提升全文檢索的並行性能,共有六張Auxiliary Table輔助表,目前每張表根據word的Latin編碼進行分區。

Auxiliary Table輔助表是持久表,存放在磁盤上,使用FTS Index Cache全文檢索縮進緩存,用來提升全文檢索的性能。FTS Index Cache是一個紅黑樹結構,其根據(word, ilist)進行排序。

​ 表數據更新後,先導入到FTS Index Cache中,可是尚未更新到Auxiliary Table中。InnoDB存儲引擎會批量對Auxiliary Table進行更新,而不是每次插入後更新一次Auxiliary Table

​ 當對全文檢索進行查詢時,Auxiliary Table首先將會在FTS Index Cache中對應的word字段合併到Auxiliary Table中,而後再進行查詢。

​ 引擎容許用戶查看指定倒排索引的Auxilary Table中分詞的信息,能夠經過設置參數innodb_ft_aux_table來觀察倒排索引的Auxiliary Table

set global innodb_ft_aux_table = 'test/fts_a';

​ 設置完成後,能夠經過查詢information_scheme架構下的表Innodb_ft_index_table獲得表fts_a的分詞信息。

​ 對於InnoDB存儲引擎而言,老是在事務提交時將分詞寫入到FTS Index Cache,而後再經過批量更新寫入到磁盤。因此在數據庫關閉時,會將FTS Index Cache同步到Auxiliary Table中。若是發生宕機的話,下次重啓數據庫時,當用戶對錶進行全文索引(查詢或插入操做時)時,會自動讀取未完成的文檔,而後進行分詞操做,再將分詞的結果放入到FTS Index Cache中。

​ 參數innodb_ft_cache_size用來控制FTS Index Cache的大小,默認值爲32M。當緩存滿時,會將其中的分詞信息同步到磁盤的輔助表中。增大參數能夠提升全文檢索的性能。可是宕機的時候,未同步到磁盤的索引信息須要更長的時間恢復。

FTS Document ID是另一個重要的概念。爲了支持全文檢索,必須有一個列與word進行映射,在InnoDB存儲引擎重這個列被命名爲FTC_DOC_ID,其類型必須是BIGINT UNSIGNED NOT NULL,而且Innodb存儲引擎自動會在該列上加入一個名爲FTS_DOC_ID_INDEXUnqiue Index。上述操做都由引擎本身完成。

​ 對於刪除操做,在事務提交時,只刪除FTS Cache Index中的記錄,對於Auxiliary Table中被刪除的記錄,會記錄下其FTS Documont ID,並將其保存在DELETED auxiliary table中。在設置參數innodb_ft_aux_table後,用戶一樣能夠訪問information_scheme架構下的表INNODB_FT_DELETED來觀察刪除的FTS Document ID

​ 由於文檔的DML操做實際並不刪除索引中的數據,反而還會在對應的DELETED表中插入數據,所以索引會變的很是大。從因此中清理已經刪除的記錄,命令是OPTIMIZE TABLE。可是這個命令會進行一些其餘操做,若是進但願對倒排索引進行操做,經過設置參數innodb_optimize_fulltext_only進行設置。

set global innodb_optimize_fulltext_only = 1;
optimize table fts_a;

​ 若被刪除的文檔很是多,那麼OPTIMIZE TABLE操可能須要佔用很是多的時間,會影響程序的併發性,能夠設置參數innodb_ft_num_word_optimize來限制每次實際刪除的分詞數量。默認值爲2000。

一個demo:

​ 建立表,添加全文檢索

CREATE TABLE fts_a(
    FTS_DOC_ID BIGINT UNSIGNED AUTO_INCREMENT NOT NULL,
    body TEXT,
    PRIMARY KEY(FTS_DOC_ID);
)

INSERT INTO fts_a
    select null, 'Pease porrideg in the pot';
    
INSERT INTO fts_a
    select null, 'Pease porrideg in the hot, pease porridege cold';
    
INSERT INTO fts_a
    select null, 'Nine days old';
    
CREATE FULLTEXT INDEX idx_fts ON fts_a(body);

​ 經過設置參數查看分詞對應信息:

set global innodb_ft_aux_table='test/fts_a';
select * from information.innodb_ft_index_table;

​ 刪除FTS_DOC_ID爲3的文檔。

DELETE FROM test.fts_a where fts_doc_id = 3;

​ 並不會直接刪除索引中對應的數據,而是將刪除的文檔ID插入到DELETED表,所以能夠進行查詢

select * from innodb_ft_deleted;

​ 若是要完全刪除倒排索引中改文檔的分詞信息。執行

set global innodb_optimize_fulltext_only = 1;
optimize table test.fts_a;
select * from innodb_ft_deleted;
select * from innodb_ft_being_deleted;

​ 運行OPTIMIZE TABLE能夠將記錄進行完全的刪除,而且完全刪除的文檔ID會記錄到表INNODB_FT_BEING_DELETED中。此外,被刪除的文檔ID,不容許再次進行插入。

stopword列表,表示在該列表中的word不須要進行索引分詞操做。默認的表在information_schema下的INNODB_FT_DEFAULT_STOPWORD,默認共有36個stopword。此外用戶也能夠經過參數innodb_ft_server_stopword_table來自定義stopword列表。

create table user_stopword (
    value varchar(30)
)ENGINE = INNODB;

SET GLOBAL 
innodb_ft_server_stopword_table = "test/user_stopword";

​ INNODB全文檢索的限制:

  • 每張表只嗯呢該有一個全文檢索的索引
  • 由多列組合而成的全文索引列沒必要須使用相同的字符集與排序規則
  • 不支持沒有單詞界定符的語言:如中文、韓文、日文等。

全文檢索

​ 語法爲:

MATCH(col1, col2, ...) AGAINST (expr (serch_modifier))
search_modifier:
{
    IN NATURAL LANGUAGE MODE
    | IN NATURAL LANGUAGE MODE WITH QUERY EXPANSION
    | IN BOOLEAN MODE
    | WITH QUERY EXPANSIONS
}
  1. Natural Language

    ​ 經過MACTH函數進行查詢,默認採用的模式,表示查詢帶有指定word的文檔。

    select * from fts_a where match(body) against('Porridge' IN NATURAL LANGUAGE MODE)
    
    select * from fts_a where match(body) against('Porridge');

    ​ 在where條件中使用MATCH函數,其返回結果是根據相關性進行降序排序的,即相關性最高的結果放在第一位。相關性的值是一個非負的浮點數字。0表示咩有任何相關行。

    ​ 相關性的計算依據的條件:

    • word是否在文檔中出現
    • word在文檔中出現的次數
    • word在索引列中的數量
    • 多少個文檔包含該word

    經過SQL語句查看相關性:

    select fts_doc_id, body, 
    Match(body) against('Porridge' IN NATURAL LANGUAGE MODE) as Relevance
    from fts_a;

    ​ INNODB存儲引擎的全文檢索,還須要考慮一下的因素:

    • 查詢的word在stopword列中,忽略該字符串的查詢
    • 查詢的word的字符長度是否在區間[innodb_ft_min_token_size, innodb_ft_max_token_size]內,不在內部,忽略該字符串的查詢。
  2. Boolean

    ​ 使用IN BOOLEAN MODE修飾符時,查詢字符串的先後字符會有特殊的含義。例如

    #pease這個字符串必定存在但hot這個字符串不存在
    select * from fts_a
    where match(body) against ('+Pease -hot' in boolean mode)\G;

    Boolean全文索引支持如下幾種操做符:

    • +表示該word必須存在

    • -表示該word必須被排除

    • (no operator)表示該word是可選的,可是若是出現,相關性會更高

    • @distance表示查詢的多個單詞之間的距離是否在distance以內,distance的單位是字節。這種全文檢索的查詢也成爲Proximity Search,例如

      match(body) against ('"Pease pot"@30' IN BOOLEAN MODE)

      表示字符串Pease和hot之間的距離須要在30字節內。

    • '>'表示出現該單詞增長相關性

    • '<'表示出現該單詞下降相關性

    • '~'表示容許出現該單詞,可是出現時相關性爲負(全文檢索查詢容許負相關性)。

    • '*'表示以該單詞開的單詞

    • "表示短語。

  3. Query Expansion

    ​ 支持全文索引的擴展查詢。經過在查詢短語中添加WITH QUERY EXPANSIONIN NATURAL LANGUAGE MODE WITH QUERY EXPANSION能夠開啓bind query expansion,查詢分爲兩個階段:

    • 第一階段:根據搜索的單詞進行全文索引查詢。
    • 第二階段:根據第一階段產生的分詞再進行一次全文檢索的查詢
相關文章
相關標籤/搜索