InnoDB全文索引:N

Wesley13
• 阅读 683

InnoDB默认的全文索引parser非常合适于Latin,因为Latin是通过空格来分词的。但对于像中文,日文和韩文来说,没有这样的分隔符。一个词可以由多个字来组成,所以我们需要用不同的方式来处理。在MySQL 5.7.6中我们能使用一个新的全文索引插件来处理它们:n-gram parser.

什么是N-gram?

在全文索引中,n-gram就是一段文字里面连续的n个字的序列。例如,用n-gram来对”信息系统”来进行分词,得到的结果如下:

N-gram 例子

1

2

3

4

N=1 : '信', '息', '系', '统';

N=2 : '信息', '息系', '系统';

N=3 : '信息系', '息系统';

N=4 : '信息系统';

如何在InnoDB中使用N-gram Parser?

N-gram parser是默认加载到MySQL中并可以直接使用的。我们只需要在DDL中创建全文索引时使用WITH PARSER ngram。比如,下面的SQL语句在MySQL 5.7.6及更高版本上可以运行。

N-gram DDL示例

1

2

3

4

5

6

7

8

9

mysql > CREATE TABLE articles

(

        FTS_DOC_ID BIGINT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,

        title VARCHAR(100),

        FULLTEXT INDEX ngram_idx(title) WITH PARSER ngram

) Engine=InnoDB CHARACTER SET utf8mb4;

Query OK, 0 rows affected (0.06 sec)

mysql> # ALTER TABLE articles ADD FULLTEXT INDEX ngram_idx(title) WITH PARSER ngram;

mysql> # CREATE FULLTEXT INDEX ngram_idx ON articles(title) WITH PARSER ngram;

我们引入了一个新的全局变量叫ngram_token_size。由它来决定n-gram中n的大小,也就是词的大小。它的默认值是2,这个时候,我们使用的是bigram。它的合法的取值范围是1到10。现在,我们很自然会想到一个问题:实际应用中应该如何设置ngram_token_size值的大小呢?当然,我们推荐使用2。但是你也可以通过如下这个简单的规则来可以选择任何合法的值:设置到你希望能查询到的最小的词的大小。如果你想查询到单个字,那么我们需要设置为1。 ngram_token_size的值设置的越小,全文索引占用的空间也越小。一般来说,查询正好等于ngram_token_size的词,速度会更快,但是查询比它更长的词或短语,则会变慢。

N-gram分词处理

N-gram parser和系统默认的全文索引parser有如下不同点:

  1. 词大小检查:因为有了ngram_token_size,所以innodb_ft_min_token_sizeinnodb_ft_max_token_size将不适用于n-gram。
  2. 无用词(stopword)处理:通常,对于一个新的词,我们会查找stopwords表,看是否有匹配的词。如果有,这个词就不会加入到全文索引中。但是在n-gram中,我们会查找stopwords表,看是否包含里面的词。这样处理的原因是,在中日韩的文本中,有很多没有意义的字符,词语和标点符号。比如,如果我们把‘的’加入到stopwords表中,那么对于句子‘信息的系统’,在默认情况下我们分词结果为‘信息’,‘系统’。其中‘息的’和‘的系’被过滤掉了。

我们可以通过查询INFORMATION_SCHEMA.INNODB_FT_INDEX_CACHEINFORMATION_SCHEMA.INNODB_FT_TABLE_TABLE来查询哪些词在全文索引里面。这是一个非常有用的调试工具。如果我们发现一个包含某个词的文档,没有如我们所期望的那样出现在查询结果中,那么这个词可能是因为某些原因不在全文索引里面。比如,它含有stopword,或者它的大小小于ngram_token_size等等。这个时候我们就可以通过查询这两个表来确认。下面是一个简单的例子:

简单的调试示例

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

mysql> INSERT INTO articles (title) VALUES ('信息系统');

Query OK, 1 row affected (0.01 sec)

mysql> SET GLOBAL innodb_ft_aux_table="test/articles";

Query OK, 0 rows affected (0.00 sec)

mysql> SELECT * FROM INFORMATION_SCHEMA.INNODB_FT_INDEX_CACHE;

+--------+--------------+-------------+-----------+--------+----------+

| WORD   | FIRST_DOC_ID | LAST_DOC_ID | DOC_COUNT | DOC_ID | POSITION |

+--------+--------------+-------------+-----------+--------+----------+

| 信息   |            1 |           1 |         1 |      1 |        0 |

| 息系   |            1 |           1 |         1 |      1 |        3 |

| 系统   |            1 |           1 |         1 |      1 |        6 |

+--------+--------------+-------------+-----------+--------+----------+

3 rows in set (0.00 sec)

N-gram查询处理

文本查询(Text Searches)

  • 在自然语言模式(NATURAL LANGUAGE MODE)下,文本的查询被转换为n-gram分词查询的并集。例如,(‘信息系统’)转换为(‘信息 息系 系统’)。下面一个例子:

    自然语言模式示例

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    mysql> INSERT INTO articles (title) VALUES ('信息系统'), ('信息 系统'), ('信息的系统'), ('信息'), ('系统'), ('息系');

    Query OK, 6 rows affected (0.01 sec)

    Records: 6  Duplicates: 0  Warnings: 0

    mysql> SELECT * FROM articles WHERE MATCH (title) AGAINST ('信息系统' IN NATURAL LANGUAGE MODE);

    +------------+-----------------+

    | FTS_DOC_ID | title           |

    +------------+-----------------+

    |          1 | 信息系统        |

    |          6 | 息系            |

    |          2 | 信息 系统       |

    |          3 | 信息的系统      |

    |          4 | 信息            |

    |          5 | 系统            |

    +------------+-----------------+

    6 rows in set (0.01 sec)

  • 在布尔模式(BOOLEAN MODE),文本查询被转化为n-gram分词的短语查询。例如,(‘信息系统’)转换为(“‘信息 息系 系统'”)。

    布尔模式示例

    1

    2

    3

    4

    5

    6

    7

    mysql> SELECT * FROM articles WHERE MATCH(title) AGAINST('信息系统' IN BOOLEAN MODE);

    +------------+--------------+

    | FTS_DOC_ID | title        |

    +------------+--------------+

    |          1 | 信息系统     |

    +------------+--------------+

    1 row in set (0.00 sec)

通配符查询(Wildcard Searches)

  • 如果前缀的长度比ngram_token_size小,那么查询结果将返回在全文索引中所有以这个词作为前缀的n-gram的词。

    通配符查询示例-1

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    mysql> SELECT * FROM articles WHERE MATCH (title) AGAINST ('信*' IN BOOLEAN MODE);

    +------------+-----------------+

    | FTS_DOC_ID | title           |

    +------------+-----------------+

    |          1 | 信息系统        |

    |          2 | 信息 系统       |

    |          3 | 信息的系统      |

    |          4 | 信息            |

    +------------+-----------------+

    4 rows in set (0.00 sec)

  • 如果前缀的长度大于等于ngam_token_size,那么这个查询则转换为一个短语(phrase search),通配符则被忽略。例如,(‘信息*’)转换为(‘”信息”‘),(‘信息系*’)转换为(‘”信息 息系”‘)。

    通配符查询示例-2

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    mysql> SELECT * FROM articles WHERE MATCH (title) AGAINST ('信息*' IN BOOLEAN MODE);

    +------------+-----------------+

    | FTS_DOC_ID | title           |

    +------------+-----------------+

    |          1 | 信息系统        |

    |          2 | 信息 系统       |

    |          3 | 信息的系统      |

    |          4 | 信息            |

    +------------+-----------------+

    4 rows in set (0.00 sec)

    mysql> SELECT * FROM articles WHERE MATCH (title) AGAINST ('信息系*' IN BOOLEAN MODE);

    +------------+--------------+

    | FTS_DOC_ID | title        |

    +------------+--------------+

    |          1 | 信息系统     |

    +------------+--------------+

    1 row in set (0.00 sec)

短语查询(Phrase Searches)

  • 短语查询则被转换为n-gram分词的短语查询。比如,(‘信息系统’)转换为(‘”信息 息系 系统”‘)。

    短语查询示例

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    mysql> SELECT * FROM articles WHERE MATCH (title) AGAINST('"信息系统"' IN BOOLEAN MODE);

    +------------+--------------+

    | FTS_DOC_ID | title        |

    +------------+--------------+

    |          1 | 信息系统     |

    +------------+--------------+

    1 row in set (0.00 sec)

    mysql> SELECT * FROM articles WHERE MATCH (title) AGAINST ('"信息 系统"' IN BOOLEAN MODE);

    +------------+---------------+

    | FTS_DOC_ID | title         |

    +------------+---------------+

    |          2 | 信息 系统     |

    +------------+---------------+

    1 row in set (0.01 sec)

如果您想了解更多关于InnoDB全文索引的详细内容,可以参考用户手册中InnoDB全文索引的部分,还有Jimmy在Dr. Dobb上的精彩文章。如果您想了解更多关于n-gram的详细内容,则可以参考用户手册中n-gram parser的部分。

我们很高兴在MySQL 5.7全文索引中增强对中日韩文的支持,这也是我们工作中很重要的部分,希望这个功能对大家有帮助。如果您有任何问题,可以在本blog中进行评论,提交一个服务需求,或者提交一个bug报告

最后,感谢您使用MySQL!

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这