MySQL5.6后,除了 MyISAM 存储引擎,事务型的 Innodb 存储引擎也支持创建和使用全文索引了。
以下为测试过程:
--创建测试表
CREATE TABLE article (
id INT AUTO_INCREMENT NOT NULL PRIMARY KEY,
title VARCHAR(200),
body TEXT
) engine=innodb;
--插入测试值:
insert into article values(null,'MySQL数据库权威指南','非常不错的书籍,值得一看');
insert into article values(null,'Oracle数据库精选','不妨看看');
insert into article values(null,'SQL Servr 数据库进阶','不容错过');
insert into article values(null,'postgreq 数据库进阶','知道了吗');
--创建复合键title,body全文索引(当然也可以创建单键)
create fulltext index ft_idx on article(title,body) with parser ngram; --使用 ngram 解释器
--测试是否能走上全文索引的执行计划
mysql> explain select * from article where match(title,body) against('精 妨');
+----+-------------+---------+------------+----------+---------------+--------+---------+-------+------+----------+-------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+----------+---------------+--------+---------+-------+------+----------+-------------------------------+
| 1 | SIMPLE | article | NULL | fulltext | ft_idx | ft_idx | 0 | const | 1 | 100.00 | Using where; Ft_hints: sorted |
+----+-------------+---------+------------+----------+---------------+--------+---------+-------+------+----------+-------------------------------+
以上所示,确认可以走上全文索引
--执行使用全文索引的 SQL,看是否能搜索到相关数据:
mysql> select * from article where match(title,body) against('精 妨');
Empty set (0.00 sec)
如上所示,结果是,搜索不到记录,怎么回呢?
往下看:
因为, mysql 5.7.9 中默认 ngram_token_size 参数的默认值为2,如下:
mysql> show variables like '%token%';
+--------------------------+-------+
| Variable_name | Value |
+--------------------------+-------+
| innodb_ft_max_token_size | 84 |
| innodb_ft_min_token_size | 3 |
| ngram_token_size | 2 |
+--------------------------+-------+
ngram_token_size 参数指的是,以ngram解释器搜索全文索引时,指定的搜索字符数最小为2个字符(非字节),但以上SQL
select * from article where match(title,body) against('精 妨'); 表示 title like '%精%' and body like '%妨%' 各只有一个字符,所以搜索不到。(意思是和用like 一样,但不能以like方式书写,这样是走不到全文索引的)。
解决方法:
1、修改 ngram_token_size 参数值:
ngram_token_size是静态(只读)参数,必须重启mysql服务,在启动mysql时,指定 --ngram_toke_size=1 的方式启动mysql服务:
[root@dg-st tmp]#mysqld_safe --user=mysql --ngram_token_size=1 &
mysql> show variables like '%token%';
+--------------------------+-------+
| Variable_name | Value |
+--------------------------+-------+
| innodb_ft_max_token_size | 84 |
| innodb_ft_min_token_size | 3 |
| ngram_token_size | 1 | #参数值已经更改为1了
+--------------------------+-------+
2、必须要重建全文索引:
create fulltext index ft_idx on article(title,body) with parser ngram;
这时,再执行以上SQL,就可以查到了:
mysql>select * from article where match(title,body) against('精 妨');
+----+-----------------------+--------------+
| id | title | body |
+----+-----------------------+--------------+
| 3 | Oracle数据库精选 | 不妨看看 |
+----+-----------------------+--------------+
1 row in set (0.01 sec)
另外一种创建全文索引的方式,可以不使用 with ngram 子句,如下:
create fulltext index ft_idx on article(title,body);
这样创建的全文索引最小和最大搜索字符数(分词)受 ft_min_word_len 和 ft_max_word_len 参数的影响,这两个参数也是静态参数,设置后,也必须要重启 mysq 服务才能生效,如下:
mysql>show variables like 'ft_%';
+--------------------------+----------------+
| Variable_name | Value |
+--------------------------+----------------+
| ft_boolean_syntax | + -><()~*:""&| | --该选项用于全文搜索特殊匹配的表达式
| ft_max_word_len | 84 |
| ft_min_word_len | 4 |
| ft_query_expansion_limit | 20 |
| ft_stopword_file | (built-in) |
+--------------------------+----------------+
所以,搜索时,全文索引的各个字段必须最小包含4个字符才能搜索到,如下:
mysql>explain select * from article where match(title,body) against('数据库精 不妨看看');
+----+-------------+---------+------------+----------+---------------+--------+---------+-------+------+----------+-------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+----------+---------------+--------+---------+-------+------+----------+-------------------------------+
| 1 | SIMPLE | article | NULL | fulltext | ft_idx | ft_idx | 0 | const | 1 | 100.00 | Using where; Ft_hints: sorted |
+----+-------------+---------+------------+----------+---------------+--------+---------+-------+------+----------+-------------------------------+
1 row in set, 1 warning (0.00 sec)
mysql> select * from article where match(title,body) against('数据库精 不妨看看');
+----+-----------------------+--------------+
| id | title | body |
+----+-----------------------+--------------+
| 3 | Oracle数据库精选 | 不妨看看 |
+----+-----------------------+--------------+
(完)