在mysql 5.6之前的版本,MyISAM引擎支持全文索引,但是InnoDB不支持;从mysql 5.6开始,InnoDB支持全文索引,但是不支持中文分词;从mysql 5.7.6开始,mysql内置了ngram分词器,可支持中文、日语等亚洲语种的分词。
全文索引fulltext index,可用于char/varchar/text列。在需要进行模糊查询时,如果使用 like “%target%” 进行查询,无法使用到索引。如果文本较大,为了优化效率,可以考虑全文索引。
本文将依次介绍以下内容:
- 分词与倒排索引,了解全文索引的基本原理
- mysql分词器配置与全文索引的创建
- 全文索引的使用示例
分词与倒排索引
我们在数据结构学习时就了解到,倒排索引的典型应用场景是搜索引擎。为了快速的根据文本找出包含此文本的文档,那么需要解决两个基本的问题:
- 对文档进行分词,找出每个文档包含的“关键字”,称为token。比如:存在一个ID=1的文档内容为“倒排索引的典型应用场景是搜索引擎”,存在的一种分词结果可以是 “倒排索引/的/典型/应用/场景/是/搜索引擎”,如此经过分即能得到这一段文本的关键字
- 构建倒排索引表,记录 关键字-所属文档 的这一基本的关联关系,以及:关键字出现次数,即词频;关键字在文档中所处的位置等。接上条,将会存在 “倒排索引” -> (ID = 1) 这一索引记录
对于分词,这是一个算法领域的问题,鄙人不敢说有所了解,大家可以自行网上学习。所幸这并不影响我们的使用层面,了解达到的效果即可。
如此,构建出倒排索引表后,一个检索过程包含的基本步骤有:
- 对每条输入,我们对输入的内容进行分词,比如:全文索引,分词得到”全文”、”索引”两个token
- 在倒排索引表中进行匹配,查找出token匹配到的所有文档
- 根据匹配结果,可以找出匹配到token最多的文档;同时可以根据token在文档中的词频评估文档与token的相关度
- 通过一系列评估、排序算法得出文档最终的相关性score,得到最终结果
倒排索引对大量文本情况下关键字到文档的检索非常有效。但是构建的过程是比较复杂的(对比正向索引);关键的是在文档更新时倒排索引表的更新会很复杂,对现有倒排索引表更新的逻辑复杂,重建倒排索引表虽然逻辑相对而言简单,但是代价高。
mysql分词器配置与全文索引的创建
mysql的全文索引即是倒排索引的应用。
在建表时可以指定添加全文索引:
1 | CREATE TABLE `test_fulltext_idx_tbl` ( |
指定了全文索引在mysql 5.7以上使用ngram分词器
但是如上对倒排索引的分析,倒排索引的更新复杂、代价高,因此在一个频繁修改的字段上使用全文索引会影响性能。因此,相比于建表同时创建索引、然后导入数据,更为高效的方式是建表时不添加全文索引、在数据导入完成后再添加全文索引:
1 | ALTER TABLE test_fulltext_idx_tbl ADD FULLTEXT ft_search_index(doc_name, content) WITH parser ngram; |
对于ngram,需要关注的是mysql的ngram_token_size这一配置。ngram_token_size表示ngram分词器分词的最小字数,默认为2,此时使用全文索引匹配单个字是无法匹配成功的。
这一配置可以在mysql的cnf文件中设置:
1 | [mysqld] |
因为中文词最少包含两个字,因此一般使用默认值2即可。
全文索引的使用示例
全文索引使用的语法为:
1 | MATCH (col1,col2,...) AGAINST (expr [search_modifier]) |
其中,MATCH指定的字段必须与使用的全文索引的字段一致,expr即检索的关键字,search_modifier可指定两种查询模式:
- IN NATURAL LANGUAGE MODE,默认的模式,自然语言模式,根据分词模糊匹配
- IN BOOLEAN MODE,布尔模式,可以在表达式中使用操作符,如 ‘+能力 -审核”,表示必须存在“能力”、必须不存在“审核”
在上文的测试表test_fulltext_idx_tbl中添加测试数据并对doc_name、content两个字段创建全文索引
1 | INSERT INTO `test_fulltext_idx_tbl` (`id`, `doc_name`, `content`) VALUES (1, '入门指南', '赋能开放平台目前已接入了各种技术能力,各个子应用根据以下步骤即可快速接入: |
使用以下语句可以通过全文索引进行检索:
1 | SELECT |
以上sql中score是文档的相关性得分,只有超过0的文档才认为匹配成功。这一查询将得到两条结果。
而因为我们使用了默认的ngram_token_size为2,即分词最小字数为2,所以可以看到以下sql得到所有文档的score均为0:
1 | SELECT |
如上文所述,检索的过程中将对输入的内容进行分词后再检索匹配,所以以下sql也能筛选出两条结果:
1 | SELECT |
实际上,’技术能力开放’并没有作为整体出现在任何一个文档中,以下like查询的sql结果为0条可以验证:
1 | SELECT |
那么如果解决需要严格匹配而不是模糊查询的情况呢?此时使用布尔模式查询配合操作符’+’就可以做到:
1 | SELECT |
可以看到,这一sql查询结果中所有文档的score均为0,均未匹配成功