vlambda博客
学习文章列表

MySQL内置全文检索

概述

版本说明

  1. MySQL5.5中,只有MyISAM支持全文检索。
  2. MySQL5.6中,InnoDB开始支持全文检索。
  3. MySQL5.7中,可以使用N-Gram插件支持全文检索。此插件支持中/日/韩文。

全文检索的使用限制

  1. 只能在类型为CHAR、VARCHAR或者TEXT的字段上创建全文索引。
  2. 只支持InnoDB和MyISAM引擎。
  3. 一个表只能建立 一个全文检索字段。如需要检索多个字段,需要将多个字段一起创建 一个索引。

N-Gram解析器

  1. MySQL中使用全局变量"ngram_token_size"来配置N-Gram中n的大小;取值范围:1~10,默认值:2。
  2. 通常将ngram_token_size的值设置为要查询的单词的最小字数。如果需要搜索单字,就要把ngram_token_size设置为1。在默认值是2的情况下,搜索单字是得不到任何结果的。
  3. 中文单词最少是两个汉字,推荐使用默认值2。

参数设置

修改MySQL的配置文件:my.ini或my.cnf

[client]
ft_min_word_len=2

[mysqld] 
ft_min_word_len=2
ngram_token_size=2

修改后记得重启MySQL服务

全文检索操作

创建索引

# 建表时创建
CREATE TABLE t_member (
    `id` INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
    `cn_name` VARCHAR(100),
    `remark` TEXT,
    FULLTEXT `ft_idx_1`(`cn_name`, `remark`) WITH PARSER ngram
) ENGINE = INNODB;

# 修改表时添加
ALTER TABLE t_member 
ADD FULLTEXT INDEX `ft_idx_1`(`cn_name`,`remark`)  
WITH PARSER ngram;

# 直接创建
CREATE FULLTEXT INDEX `ft_idx_1` 
ON t_member (`cn_name`,`remark`) 
WITH PARSER ngram;

删除索引

DROP INDEX `ft_idx_1` ON t_member;

重建索引

只适用于MyISAM引擎,在修改表的全文检索设置后执行。

repair table t_member quick;

全文检索的使用

基础语法

SELECT <字段表> FROM <表名> 
WHERE 
MATCH(字段) AGAINST ('关键词' 搜索模式);

注意:MATCH中的字段要与全文检索定义时的字段个数相同。

# 全文检索定义了`cn_name`,`remark`字段,所以在MATCH中要把这两个字段都写上
SELECT `cn_name`, `remark` 
FROM t_member 
WHERE 
MATCH(`cn_name`, `remark`) 
AGAINST('张三');

查询匹配度

可用于查询数据的匹配度

SELECT `cn_name`, `remark`, 
MATCH(`cn_name`, `remark`) AGAINST('张三'
FROM t_member;

全文检索模式

1. 自然语言模式(NATURAL LANGUAGE MODE)

MySQL默认的全文检索模式。此模式不能使用操作符,用于简单查询。

2. BOOLEAN模式(BOOLEAN MODE)

此模式可以使用操作符,可以支持指定关键词必须出现或者必须不能出现或者关键词权重高低等复杂查询。

50%的限制

在使用全文检索时,常提到“50%的限制”。搜到一段解释如下:

剔除一半匹配行以上都有的词,譬如说,每个行都有this这个字的话,那用this去查时,会找不到任何结果,这在记录条数特别多时很有用,原因是数据库认为把所有行都找出来是没有意义的,这时,this几乎被当作是stopword(中断词);但是若只有两行记录时,是啥鬼也查不出来的,因为每个字都出现50%(或以上),要避免这种状况,请用IN BOOLEAN MODE。

但是在测试时,使用中文关键词进行查询,即使每条记录中都有关键词,也能查询到结果(难道是我测试数据有问题?还是说这50%的限制只是对英文有效?)。

BOOLEAN模式中的语法

  1. +:一定要有(不含有该关键词的数据条均被忽略)。
  2. -:不可以有(排除指定关键词,含有该关键词的均被忽略)。
  3. >:提高该条匹配数据的权重值。
  4. <:降低该条匹配数据的权重值。
  5. ~:将其相关性由正转负,表示拥有该字会降低相关性(但不像“-”将之排除),只是排在较后面,权重值降低。
  6. *:万用字,要接在查询关键词后面。
  7. "":用双引号表示要查询内容要完全相符,不可拆字。

举例

# 无操作符,表示或
SELECT `cn_name`, `remark` 
FROM t_member  
WHERE 
MATCH(`cn_name`, `remark`) 
AGAINST ('一般 搜索' IN BOOLEAN MODE);

# 必须同时包含“一般”和“搜索”
SELECT `cn_name`, `remark` 
FROM t_member  
WHERE 
MATCH(`cn_name`, `remark`) 
AGAINST ('+一般 +搜索' IN BOOLEAN MODE);

# 必须包含“搜索”,但是如果包含“一般”,相关性会更高。
SELECT `cn_name`, `remark` 
FROM t_member  
WHERE 
MATCH(`cn_name`, `remark`) 
AGAINST ('+搜索 一般' IN BOOLEAN MODE);

# 必须包含“一般”,同时不能包含“搜索”。
SELECT `cn_name`, `remark` 
FROM t_member  
WHERE 
MATCH(`cn_name`, `remark`) 
AGAINST ('+一般 -搜索' IN BOOLEAN MODE);

# 必须包含“搜索”,但是如果也包含“一般”的话,相关性要比不包含“一般”的记录低。
SELECT `cn_name`, `remark` 
FROM t_member  
WHERE MATCH(`cn_name`, `remark`) 
AGAINST ('+搜索 ~一般' IN BOOLEAN MODE);

# 查询必须包含“一般”“简单”或者“一般”“搜索”的记录,但是“一般”“简单”的相关性要比“一般”“搜索”高。
SELECT `cn_name`, `remark` 
FROM t_member  
WHERE 
MATCH(`cn_name`, `remark`) 
AGAINST ('+一般 +(>简单 <搜索)' IN BOOLEAN MODE);

# *:星号,查询包含以搜索开头的单词的记录。
SELECT `cn_name`, `remark` 
FROM t_member  
WHERE 
MATCH(`cn_name`, `remark`) 
AGAINST ('搜索*' IN BOOLEAN MODE);

# 双引号,把要搜素的词括起来,效果类似于like '%some words%', 
# 例如“some words of wisdom”会被匹配到,而“some noise words”就不会被匹配。
# 但是对中文来说,感觉效果一般。
## 没有双引号,有结果
SELECT `cn_name`, `remark` 
FROM t_member  
WHERE 
MATCH(`cn_name`, `remark`) 
AGAINST ('一般 简单 搜索' IN BOOLEAN MODE);
## 有双引号,没结果,一般中文不会这么查询吧
SELECT `cn_name`, `remark` 
FROM t_member  
WHERE 
MATCH(`cn_name`, `remark`) 
AGAINST ('"一般 简单 搜索"' IN BOOLEAN MODE);

小结

  1. 对于小项目来说,MySQL的全文检索应该够用了,甚至like都够用了。
  2. 对全文检索有更高的需求,或者要跟上技术的更新换代,还是用ES吧 。