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,查询分为两个阶段:

    • 第一阶段:根据搜索的单词进行全文索引查询。
    • 第二阶段:根据第一阶段产生的分词再进行一次全文检索的查询
相关文章
相关标签/搜索