MySQL 5.7 InnoDB中文全文索引测试

时间:2022-09-22 23:47:59
从MySQL 5.7.6开始,MySQL内置了ngram全文检索插件,用来支持中文分词,并且对MyISAM和InnoDB引擎有效。在全文索引中,ngram就是一段文字里面连续的n个字的序列。本文将对MySQL5.7的中文全文索引做简单测试。
中文全文索引测试
在使用中文检索分词插件ngram之前,可以在MySQL配置文件my.cnf里面设置分词大小,比如,
[mysqld]
ngram_token_size=2
这里把分词大小设置为2(分词的SIZE越大,索引的体积就越大,所以要根据自身情况来设置合适的大小)
全局变量,默认2,范围:1~10,推荐2.
需要说明的是,对于词大小检查,因为有了ngram_token_size,所以innodb_ft_min_token_size和innodb_ft_max_token_size将不适用于n-gram。

mysql版本
[localhost]root@test04:18:55>select version();
+------------+
| version()  |
+------------+
| 5.7.18-log |
+------------+
1 row in set (0.00 sec)
表结构如下
CREATE TABLE `tb_customer_3` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `register_name` varchar(100) NOT NULL COMMENT '工商名',
  `brand_name` varchar(100) DEFAULT NULL COMMENT '品牌名',
  `en_name` varchar(100) DEFAULT NULL COMMENT '英文名',
  PRIMARY KEY (`id`),
  FULLTEXT KEY `fidx` (`register_name`,`brand_name`,`en_name`) with parser ngram
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
数据如下
[localhost]root@test03:52:28>select * from tb_customer_3;
+-------+--------------------------------------------------+------------+---------+
| id    | register_name                                    | brand_name | en_name |
+-------+--------------------------------------------------+------------+---------+
| 28152 | 芜湖美的厨卫电气制造有限公司                     | NULL       | NULL    |
| 28153 | 北京凡客尚品电子商务有限公司                     | NULL       | NULL    |
| 28154 | 凡客诚品(北京)科技有限公司                     | NULL       | NULL    |
| 28155 | 瞬联讯通科技(北京)有限公司                     | NULL       | NULL    |
| 28156 | 北京畅捷通讯有限公司                             | NULL       | NULL    |
| 28157 | 北京畅捷通支付技术有限公司                       | NULL       | NULL    |
| 28158 | 畅捷通信息技术股份有限公司                       | NULL       | NULL    |
| 28159 | 北京畅捷科技有限公司                             | NULL       | NULL    |
| 28160 | 中国航天工业科学技术咨询有限公司                 | NULL       | NULL    |
| 28161 | 北京·松下彩色显象管有限公司                      | NULL       | NULL    |
| 28162 | 北京·松下电子部品有限公司                        | NULL       | NULL    |
| 28163 | 北京松下照明光源有限公司                         | NULL       | NULL    |
| 28164 | 松下电气机气(北京)有限公司                     | NULL       | NULL    |
| 28165 | 中新航天科技有限公司                             | NULL       | NULL    |
| 28166 | 北京奔驰汽车有限公司                             | NULL       | NULL    |
| 28167 | 阿莫斯特环保科技(北京)有限公司                 | NULL       | NULL    |
| 28168 | 北京低碳清洁能源研究所                           | NULL       | NULL    |
| 28169 | 北京未来科技城开发建设有限公司                   | NULL       | NULL    |
| 28170 | 北京诺华制药有限公司                             | NULL       | NULL    |
| 28171 | 北京信元电信维护有限责任公司                     | NULL       | NULL    |
+-------+--------------------------------------------------+------------+---------+
20 rows in set (0.00 sec)
显式指定全文检索表源
set global innodb_ft_aux_table="test/tb_customer_3";
通过系统表,就可以查看到底是怎么划分tb_customer_3里的数据
[localhost]root@test03:53:39>SELECT * FROM information_schema.INNODB_FT_INDEX_CACHE limit 80,10; 
+--------+--------------+-------------+-----------+--------+----------+
| WORD   | FIRST_DOC_ID | LAST_DOC_ID | DOC_COUNT | DOC_ID | POSITION |
+--------+--------------+-------------+-----------+--------+----------+
| 品有   |           12 |          12 |         1 |     12 |       23 |
| 品电   |            3 |           3 |         1 |      3 |       15 |
| 品(   |            4 |           4 |         1 |      4 |        9 |
| 商务   |            3 |           3 |         1 |      3 |       24 |
| 气制   |            2 |           2 |         1 |      2 |       21 |
| 气(   |           14 |          14 |         1 |     14 |       15 |
| 国航   |           10 |          10 |         1 |     10 |        3 |
| 城开   |           19 |          19 |         1 |     19 |       18 |
| 天工   |           10 |          10 |         1 |     10 |        9 |
| 天科   |           15 |          15 |         1 |     15 |        9 |
+--------+--------------+-------------+-----------+--------+----------+
10 rows in set (0.00 sec)
文本查询
<1>在自然语言模式(NATURAL LANGUAGE MODE)下,文本的查询被转换为n-gram分词查询的并集。例如,('松下电子')转换为('松下 下电 电子')。
[localhost]root@test04:22:21>select * from tb_customer_3 where match(register_name,brand_name,en_name) against ('松下电子' IN NATURAL LANGUAGE MODE);
+-------+--------------------------------------------+------------+---------+
| id    | register_name                              | brand_name | en_name |
+-------+--------------------------------------------+------------+---------+
| 28162 | 北京·松下电子部品有限公司                  | NULL       | NULL    |
| 28164 | 松下电气机气(北京)有限公司               | NULL       | NULL    |
| 28153 | 北京凡客尚品电子商务有限公司               | NULL       | NULL    |
| 28161 | 北京·松下彩色显象管有限公司                | NULL       | NULL    |
| 28163 | 北京松下照明光源有限公司                   | NULL       | NULL    |
+-------+--------------------------------------------+------------+---------+
5 rows in set (0.01 sec)
包含关键字松下、下电、电子的公司都会显示出来。
<2>在布尔模式(BOOLEAN MODE),文本查询被转化为n-gram分词的短语查询。例如,('松下电子')转换为("'松下 下电 电子'")。
[localhost]root@test04:09:29>select * from tb_customer_3 where match(register_name,brand_name,en_name) against ('松下电子' IN BOOLEAN MODE);
+-------+----------------------------------------+------------+---------+
| id    | register_name                          | brand_name | en_name |
+-------+----------------------------------------+------------+---------+
| 28162 | 北京·松下电子部品有限公司              | NULL       | NULL    |
+-------+----------------------------------------+------------+---------+
通配符查询(Wildcard Searches)
<1>如果前缀的长度比ngram_token_size小,那么查询结果将返回在全文索引中所有以这个词作为前缀的n-gram的词。
[localhost]root@test04:30:21>select * from tb_customer_3 where match(register_name,brand_name,en_name) against ('松*' IN BOOLEAN MODE);    
+-------+--------------------------------------------+------------+---------+
| id    | register_name                              | brand_name | en_name |
+-------+--------------------------------------------+------------+---------+
| 28161 | 北京·松下彩色显象管有限公司                | NULL       | NULL    |
| 28162 | 北京·松下电子部品有限公司                  | NULL       | NULL    |
| 28163 | 北京松下照明光源有限公司                   | NULL       | NULL    |
| 28164 | 松下电气机气(北京)有限公司               | NULL       | NULL    |
+-------+--------------------------------------------+------------+---------+
4 rows in set (0.00 sec)
<2>如果前缀的长度大于等于ngam_token_size,那么这个查询则转换为一个短语(phrase search),通配符则被忽略。例如,('松下*')转换为('"松下"'),('松下电*')转换为('"松下 下电"')。
[localhost]root@test04:25:09>select * from tb_customer_3 where match(register_name,brand_name,en_name) against ('松下电*' IN BOOLEAN MODE);
+-------+--------------------------------------------+------------+---------+
| id    | register_name                              | brand_name | en_name |
+-------+--------------------------------------------+------------+---------+
| 28162 | 北京·松下电子部品有限公司                  | NULL       | NULL    |
| 28164 | 松下电气机气(北京)有限公司               | NULL       | NULL    |
+-------+--------------------------------------------+------------+---------+
2 rows in set (0.00 sec)
短语查询(Phrase Searches)
短语查询则被转换为n-gram分词的短语查询。比如,('松下电气')转换为('"松下 下电 电气"')。
[localhost]root@test06:28:59>select * from tb_customer_3 where match(register_name,brand_name,en_name) against ('"松下电气"' IN BOOLEAN MODE);
+-------+--------------------------------------------+------------+---------+
| id    | register_name                              | brand_name | en_name |
+-------+--------------------------------------------+------------+---------+
| 28164 | 松下电气机器(北京)有限公司               | NULL       | NULL    |
+-------+--------------------------------------------+------------+---------+
1 row in set (0.00 sec)

全文索引停词
停词(STOP WORD)用于在分词时忽略那些常见的不重要的单词,InnoDB目前内建的停词可以从information_schema.INNODB_FT_DEFAULT_STOPWORD读取,用户也可以自己定义停词列表,方法如下
CREATE TABLE crm_stopwords(value VARCHAR(30)) ENGINE = INNODB;
insert into crm_stopwords values ('公司');
SET GLOBAL innodb_ft_server_stopword_table = 'test/crm_stopwords';
注意,上述操作需在创建全文索引前完成且在指定所使用的停止字表时,表必须已经存在。

注意事项
<1>MATCH()函数中的列必须与FULLTEXT索引中的列相同。如MATCH(register_name,brand_name,en_name)与FULLTEXT(register_name,brand_name,en_name)。若要单独搜索某列,如register_name列,则需另外单独为该列建全文索引FULLTEXT(register_name),然后用MATCH(register_name)搜索。
<2>创建全文索引时会加共享锁,不支持并发DML
<3>全文索引一定要加上with parser ngram

查找所有包含全文索引的表

SELECT distinct table_schema,table_name FROM information_schema.STATISTICS WHERE index_type='fulltext';

重要

从面测试得出,如果匹配的关键字是"互联企信"则响应时间很快,响应时间为0.00秒,如果匹配的关键字是"北京互联企信"则响应时间平均是0.07秒,如果匹配的关键字是"北京互联企信信息技术有限公司南宁分公司",则响应时间平均是0.518秒。
这个原因是扫描的分词太多导致的,也就是说分词长度为2,搜索的关键字越接近2速度越快,越大于2速度越慢。
因此,可以得出结论,中文全文索引还没法在线上使用,因为用户搜索公司名称时输入的关键字不确定,可能长也可能短,因此全文索引的响应时间不确定。


参考
https://dev.mysql.com/doc/refman/5.7/en/fulltext-stopwords.html?spm=5176.100239.blogcont224.14.DNcpuZ

http://mysqlserverteam.com/innodb%E5%85%A8%E6%96%87%E7%B4%A2%E5%BC%95%EF%BC%9An-gram-parser/