一、前言

明天一个共事问我,如何应用 Mysql 实现相似于 ElasticSearch 的全文检索性能,并且对检索关键词跑分?我过后脑子里立马产生了疑难?为啥不间接用es呢?简略好用还贼快。然而听他说,数据量不多,客户给的工夫十分无限,基本没工夫去搭建es,所以还是看一下 Mysql 的全文检索性能吧!
MySQL 从 5.7.6 版本开始,MySQL就内置了ngram全文解析器,用来反对中文、日文、韩文分词。在 MySQL 5.7.6 版本之前,全文索引只反对英文全文索引,不反对中文全文索引,须要利用分词器把中文段落预处理拆分成单词,而后存入数据库。本篇文章测试的时候,采纳的 Mysql 5.7.6 ,InnoDB数据库引擎。
原文解析

二、全文解析器ngram

ngram就是一段文字外面间断的n个字的序列。ngram全文解析器可能对文本进行分词,每个单词是间断的n个字的序列。
例如,用ngram全文解析器对“你好世界”进行分词:

n=1: '你', '好', '世', '界' n=2: '你好', '好世', '世界' n=3: '你好世', '好世界' n=4: '你好世界'

MySQL 中应用全局变量 ngram_token_size 来配置 ngram 中 n 的大小,它的取值范畴是1到10,默认值是 2。通常ngram_token_size设置为要查问的单词的最小字数。如果须要搜寻单字,就要把ngram_token_size设置为 1。在默认值是 2 的状况下,搜寻单字是得不到任何后果的。因为中文单词起码是两个汉字,举荐应用默认值 2。

咱们看一下Mysql默认的ngram_token_size大小:

show variables like 'ngram_token_size'

ngram_token_size 变量的两种设置形式:

1、启动mysqld命令时指定
mysqld --ngram_token_size=2
2、批改mysql配置文件
[mysqld] ngram_token_size=2

三、全文索引

以某文书数据为例,新建数据表 t_wenshu ,并且针对文书内容字段创立全文索引,导入10w条测试数据。

1、建表时创立全文索引

CREATE TABLE `t_wenshu` (  `province` varchar(255) DEFAULT NULL,  `caseclass` varchar(255) DEFAULT NULL,  `casenumber` varchar(255) DEFAULT NULL,  `caseid` varchar(255) DEFAULT NULL,  `types` varchar(255) DEFAULT NULL,  `title` varchar(255) DEFAULT NULL,  `content` longtext,  `updatetime` varchar(255) DEFAULT NULL,  FULLTEXT KEY `content` (`content`) WITH PARSER `ngram`) ENGINE=InnoDB DEFAULT CHARSET=utf8;

2、通过 alter table 形式

ALTER TABLE t_wenshu ADD FULLTEXT INDEX content_index (content) WITH PARSER ngram;

3、通过 create index 形式

CREATE FULLTEXT INDEX content_index ON t_wenshu (content) WITH PARSER ngram;

四、检索模式

自然语言检索

(IN NATURAL LANGUAGE MODE)自然语言模式是 MySQL 默认的全文检索模式。自然语言模式不能应用操作符,不能指定关键词必须呈现或者必须不能呈现等简单查问。

布尔检索

(IN BOOLEAN MODE)剔除一半匹配行以上都有的词,例如,每行都有this这个词的话,那用this去查时,会找不到任何后果,这在记录条数特地多时很有用,起因是数据库认为把所有行都找进去是没有意义的,这时,this简直被当作是stopword(中断词);布尔检索模式能够应用操作符,能够反对指定关键词必须呈现或者必须不能呈现或者关键词的权重高还是低等简单查问。

   ● IN BOOLEAN MODE的特色:       ·不剔除50%以上合乎的row。       ·不主动以相关性反向排序。       ·能够对没有FULLTEXT index的字段进行搜查,但会十分慢。       ·限度最长与最短的字符串。       ·套用Stopwords。   ● 搜寻语法规定:     +   肯定要有(不含有该关键词的数据条均被疏忽)。      -   不能够有(排除指定关键词,含有该关键词的均被疏忽)。      >   进步该条匹配数据的权重值。      <   升高该条匹配数据的权重值。     ~   将其相关性由正转负,示意领有该字会升高相关性(但不像-将之排除),只是排在较前面权重值升高。      *   万用字,不像其余语法放在后面,这个要接在字符串前面。      " " 用双引号将一段句子包起来示意要齐全相符,不可拆字。

查问扩大检索

正文:(WITH QUERY EXPANSION)因为查问扩大可能带来许多非相关性的查问,审慎应用!

五、检索查问

1)查问 content 中蕴含“<font color='red'>盗窃罪</font>”的记录,查问语句如下

select caseid,content, MATCH ( content) AGAINST ('盗窃罪') as score from t_wenshu where MATCH ( content) AGAINST ('盗窃罪' IN NATURAL LANGUAGE MODE)

2)查问 content 中蕴含“<font color='red'>寻衅滋事</font>”的记录,查问语句如下

select caseid,content, MATCH ( content) AGAINST ('寻衅滋事') as score from t_wenshu where MATCH ( content) AGAINST ('寻衅滋事' IN NATURAL LANGUAGE MODE) ;

3)单个汉字,查问 content 中蕴含“<font color='red'>我</font>”的记录,查问语句如下

select caseid,content, MATCH ( content) AGAINST ('我') as score from t_wenshu where MATCH ( content) AGAINST ('我' IN NATURAL LANGUAGE MODE) ;

备注:因为设置的全局变量 ngram_token_size 的值为 2。如果想查问单个汉字,须要在配置文件 my.ini 中批改 ngram_token_size = 1 ,并重启 mysqld 服务,此处不做尝试了。

4)查问字段 content 中蕴含 “<font color='red'>危险驾驶</font>”和“<font color='red'>寻衅滋事</font>”的语句如下:

select caseid,content, MATCH (content) AGAINST ('+危险驾驶 +寻衅滋事') as score from t_wenshu where MATCH (content) AGAINST ('+危险驾驶 +寻衅滋事' IN BOOLEAN MODE);

5)查问字段 content 中蕴含 “<font color='red'>危险驾驶</font>”,但不蕴含“<font color='blue'>寻衅滋事</font>”的语句如下:

select caseid,content, MATCH (content) AGAINST ('+危险驾驶 -寻衅滋事') as score from t_wenshu where MATCH (content) AGAINST ('+危险驾驶 -寻衅滋事' IN BOOLEAN MODE);

6)查问字段 conent 中蕴含“<font color='red'>危险驾驶</font>”或者“<font color='red'>寻衅滋事</font>”的语句如下:

select caseid,content, MATCH (content) AGAINST ('危险驾驶 寻衅滋事') as score from t_wenshu where MATCH (content) AGAINST ('危险驾驶 寻衅滋事' IN BOOLEAN MODE);

六、总结

1)应用 Mysql 全文索引之前,搞清楚各版本反对状况;
2)全文索引比 like + % 快 N 倍,然而可能存在精度问题;
3)如果须要全文索引的是大量数据,倡议先增加数据,再创立索引;
4)对于中文,能够应用 MySQL 5.7.6 之后的版本,或者 Sphinx、Lucene 等第三方的插件;
5)MATCH()函数应用的字段名,必须要与创立全文索引时指定的字段名统一,且只能是同一个表的字段不能跨表;