前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Mysql的全文检索

Mysql的全文检索

原创
作者头像
eeaters
修改2022-02-21 18:04:33
1.7K0
修改2022-02-21 18:04:33
举报
文章被收录于专栏:阿杰阿杰

mysql分词索引

  • 前言
  • 使用范围及限制
  • 全文检索的全局配置
  • 数据准备
  • 全文检索元数据
    • INNODB_FT_CONFIG
    • INNODB_FT_BEING_DELETED
    • INNODB_FT_DELETED
    • INNODB_FT_DEFAULT_STOPWORD
    • INNODB_FT_INDEX_CACHE
    • INNODB_FT_INDEX_TABLE
  • 全文检索查询
    • 查询模式
    • 简单查询
    • 相关度分数查询
    • 布尔全文检索
    • 全文检索扩展查询(同义词效果)
  • 自定义停用词
  • ngram全文检索器(中文停用词)

前言

可以直接跟着官方敲一下: mysql官方文档-fulltext

现在的产品一言不合就想分词或者全模糊查询,之前的解决方案有:

  1. 数据量少呀,数据都抛给前端了; 前端看着办
  2. 很多代码里面都有like 两边% 查询的,我很讨厌这种sql,但是好像大多数开发无所谓,一般很难劝的住别人
  3. 不巧自己来了这种需求, 跟产品争执一下,目的: 模糊查询很合理,不过匹配规则要稍微改改,后模糊查询的话,需求我麻溜的接了(大多数情况需求产品会退一步的)
  4. 争执失败了,产品说了某某某产品就是这样子,老板发话了我们也要; 结果链路复杂不说请求量还不见得小, 需求评估下,是否需要上es

这次碰到一个类似需求处于设计阶段,因为时间充足,需求又简单,就照着官网学习下mysql的全文检索,万一很合适的话,后面就可以多一种备用方案了…

使用范围及限制

  1. 仅支持与InnoDB和MyISAM引擎,表现形式还略有不同,没有拿MyISAM进行测试
  2. 不支持分区表
  3. 不支持Unicode编码,usc2这个字符集最好也别使用
  4. 停用词默认不支持中文,日语….
    • 基于字符的 ngram 全文检索解析器支持中日韩三种语言
    • 日语还有一个MeCab解析器插件
  5. 虽然我们可以每一行都设置一个字符集,但是全文检索相关的列必须同字符
  6. %这个用于模糊查询,全文检索不支持这个通配符; 一般会使用 word* 这样子
  7. DML(增删改)操作中,事务提交后才会正式插入到全文索引表中, 不会有脏读之类的问题

全文检索的全局配置

代码语言:txt
复制
show global VARIABLES where Variable_name like 'innodb_ft%'

Variable_name	Value
---
innodb_ft_aux_table
innodb_ft_cache_size	8000000
innodb_ft_enable_diag_print	OFF
innodb_ft_enable_stopword	ON
innodb_ft_max_token_size	84
innodb_ft_min_token_size	3
innodb_ft_num_word_optimize	2000
innodb_ft_result_cache_limit	2000000000
innodb_ft_server_stopword_table
innodb_ft_sort_pll_degree	2
innodb_ft_total_cache_size	640000000
innodb_ft_user_stopword_table

数据准备

代码语言:txt
复制
CREATE TABLE articles (
	id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
	title VARCHAR (200),
	body TEXT,
	FULLTEXT (title, body)
) ENGINE = INNODB;

INSERT INTO articles (title, body)
VALUES
	(
		'MySQL Tutorial',
		'DBMS stands for DataBase ...'
	),
	(
		'How To Use MySQL Well',
		'After you went through a ...'
	),
	(
		'Optimizing MySQL',
		'In this tutorial we show ...'
	),
	(
		'1001 MySQL Tricks',
		'1. Never run mysqld as root. 2. ...'
	),
	(
		'MySQL vs. YourSQL',
		'In the following database comparison ...'
	),
	(
		'MySQL Security',
		'When configured properly, MySQL ...'
	);

    # 后面查询表数据时需要执行下面sql才能将调试定位到这个表中
	set GLOBAL innodb_ft_aux_table = 'test/articles';

全文检索元数据

代码语言:txt
复制
SHOW TABLES FROM INFORMATION_SCHEMA LIKE 'INNODB_FT%';

Tables_in_information_schema (INNODB_FT%)
---
INNODB_FT_CONFIG
INNODB_FT_BEING_DELETED
INNODB_FT_DELETED
INNODB_FT_DEFAULT_STOPWORD
INNODB_FT_INDEX_TABLE
INNODB_FT_INDEX_CACHE

INNODB_FT_CONFIG

提供了一个InnoDB全文检索和相关处理的元信息

代码语言:txt
复制
select * from INFORMATION_SCHEMA.INNODB_FT_CONFIG

KEY	                         VALUE
---
optimize_checkpoint_limit	  180
synced_doc_id	               8
stopword_table_name
use_stopword	               1

INNODB_FT_BEING_DELETED

用于监控或者调试; 正常情况下数据为空

INNODB_FT_DELETED

存储被删除的innoDB的行;索引重组代价太大; mysql采用将删除的行进行记录,查询是会从这个结果集中进行数据过滤;

但是这个数据不是永久存在的; 当执行 OPTIMIZE TABLE articles; 时索引重组会将表里的数据干掉

INNODB_FT_DEFAULT_STOPWORD

在innoDB表中创建全文检索索引时的默认停用词列表

代码语言:txt
复制
select * from INFORMATION_SCHEMA.INNODB_FT_DEFAULT_STOPWORD

value
---
a
about
an
are
as
at
be
by
com
de
en
for
from
how
i
in
is
it
la
of
on
or
that
the
this
to
was
what
when
where
who
will
with
und
the
www

INNODB_FT_INDEX_CACHE

新插入行的时候.为避免索引重组,索引会临时存放在缓存中

我们可以通过执行 OPTIMIZE TABLE articles; 后将cache清空,索引放到 INNODB\_FT\_INDEX\_TABLE表中

代码语言:txt
复制
select * from INFORMATION_SCHEMA.INNODB_FT_INDEX_TABLE limit 5

WORD	FIRST_DOC_ID	LAST_DOC_ID	DOC_COUNT	DOC_ID	POSITION
---
1001	5	5	1	5	0
after	3	3	1	3	22
comparison	6	6	1	6	44
configured	7	7	1	7	20
database	2	6	2	2	31

INNODB_FT_INDEX_TABLE

首次insert后 , 表中并没有信息,需要执行 OPTIMIZE TABLE articles;

结构和 cache 一致

代码语言:txt
复制
##
OPTIMIZE TABLE articles;
SELECT * FROM INFORMATION_SCHEMA.INNODB_FT_INDEX_TABLE LIMIT 5;


## 上面select的结果集
WORD	FIRST_DOC_ID	LAST_DOC_ID	DOC_COUNT	DOC_ID	POSITION
---
1001        	5       	5         	1        	5   	0
after       	3        	3	        1        	3   	22
comparison   	6	        6	        1        	6   	44
configured	    7 	        7        	1         	7   	20
database	    2	        6        	2        	2   	31

全文检索查询

mysql官方示例

查询模式

代码语言:txt
复制
search_modifier:
  {
       IN NATURAL LANGUAGE MODE         -- 这个是默认的
     | IN NATURAL LANGUAGE MODE WITH QUERY EXPANSION
     | IN BOOLEAN MODE
     | WITH QUERY EXPANSION
  }

简单查询

全文索引设置了两个字段,那么就得两个一起用; 想用一个字段需要单独为一个字段设置一个全文检索的索引

全文检索有相关度排名,当满足下面条件则按相关度进行排序

  1. 没有明确的order by
  2. 必须使用全文检索执行搜索
  3. 有多表联查时,全文索引必须是连接中最左边的非常量表
代码语言:txt
复制
SELECT count(*) count FROM articles WHERE MATCH(title,body) AGAINST('database')

count
---
2

# 由于全文检索默认是进行优先级排序;count可以通过下面sql来避开排序来提升性能
SELECT    COUNT(IF(MATCH (title,body) AGAINST ('database' IN NATURAL LANGUAGE MODE), 1, NULL))    AS count    FROM articles;

分词默认不区分大小写,想区分从字符集排序规则中进行调整

相关度分数查询

  • 分词选项会进行分词
  • 没有where则所有行都会进行分数计算,如果不想有太多干扰,可以加where
代码语言:txt
复制
select id,MATCH(title,body) AGAINST ('tutorial abdc esf') as score FROM articles
# WHERE MATCH(title,body) AGAINST ('tutorial abdc esf' );

id	score
---
1	0.22764469683170319
2	0
3	0.22764469683170319
4	0
5	0
6	0

布尔全文检索

前面有提到默认是NATURAL方式进行查询; 我们可以通过布尔修饰符来调整匹配的行文, 通过查询条件前加 + 表示包含, - 表示排除

代码语言:txt
复制
select * FROM articles where MATCH(title,body) AGAINST ('+MYSQL -configured -tutorial' IN BOOLEAN MODE);

id	title	body
---
2	How To Use MySQL Well	After you went through a ...
4	1001 MySQL Tricks	1. Never run mysqld as root. 2. ...
5	MySQL vs. YourSQL	In the following database comparison ...

全文检索的一些使用操作说明:

  1. MYSQL DBMS 查找至少包含一个字符串的行
  2. +MYSQL +DBMS 查找包含两个的字符串
  3. +MYSQL DBMS 查找包含MYSQL的行,如果有DBMS则按照优先级进行排序
  4. +MYSQL -DBMS 查找包含MYSQL但是不包含DBMS的行
  5. '"MySQL Tutorial"' 双引号将词语进行组合
  6. 官网还有其他组合,比如 ~ 号,没太了解清楚,不记录了

全文检索扩展查询(同义词效果)

当我们使用 QUERY EXPANSION 模式的时候,可以实现类似es的同义词效果;

代码语言:txt
复制
SELECT * FROM articles
    WHERE MATCH (title,body)
    AGAINST ('database' IN NATURAL LANGUAGE MODE);

id	title	body
---
1	MySQL Tutorial	DBMS stands for DataBase ...
5	MySQL vs. YourSQL	In the following database comparison ...
代码语言:txt
复制
SELECT * FROM articles
    WHERE MATCH (title,body)
    AGAINST ('database' WITH QUERY EXPANSION);

id	title	body
---
5	MySQL vs. YourSQL	In the following database comparison ...
1	MySQL Tutorial	DBMS stands for DataBase ...
3	Optimizing MySQL	In this tutorial we show ...
6	MySQL Security	When configured properly, MySQL ...
2	How To Use MySQL Well	After you went through a ...
4	1001 MySQL Tricks	1. Never run mysqld as root. 2. ...

自定义停用词

前面检索元数据中有提到的 INNODB_FT_DEFAULT_STOPWORD 是mysql的默认停用词; 不过停用词可以自定义, 但是字段是必须得是value

停用词是否区分大小写和服务器的排序规则有关,比如: latin1_swedish_ci 不区分大消息, latin1_general_cs / latin1_bin 就区分大小写

代码语言:txt
复制
CREATE TABLE my_stopwords(value VARCHAR(25)) ENGINE INNODB;
INSERT into my_stopwords (value) values ('Ishmael'),('Ralph');

# 将新表设置为停用词使用的表
SET GLOBAL innodb_ft_server_stopword_table = 'test/my_stopwords';

# 新建另一张表进行测试
CREATE TABLE `opening_lines` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `opening_line` text,
  `author` varchar(200) DEFAULT NULL,
  `title` varchar(200) DEFAULT NULL,
  PRIMARY KEY (`id`),
  FULLTEXT KEY `ft_opening_lines` (`opening_line`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;


INSERT INTO opening_lines (opening_line, author, title)
VALUES
	(
		'Call me Ishmael.',
		'Herman Melville',
		'Moby-Dick'
	),
	(
		'A screaming comes across the sky.',
		'Thomas Pynchon',
		'Gravity\'s Rainbow'
	),
	(
		'I am an invisible man.',
		'Ralph Ellison',
		'Invisible Man'
	),
	(
		'Where now? Who now? When now?',
		'Samuel Beckett',
		'The Unnamable'
	),
	(
		'It was love at first sight.',
		'Joseph Heller',
		'Catch-22'
	),
	(
		'All this happened, more or less.',
		'Kurt Vonnegut',
		'Slaughterhouse-Five'
	),
	(
		'Mrs. Dalloway said she would buy the flowers herself.',
		'Virginia Woolf',
		'Mrs. Dalloway'
	),
	(
		'It was a pleasure to burn.',
		'Ray Bradbury',
		'Fahrenheit 451'
	);

ngram全文检索器(中文停用词)

默认停用词大小为2; 修改值需要mysql启动的时候指定: mysqld --ngram_token_size=n 测试一个默认为2的效果

这里需要注意, 虽然默认停用词都是英文的; 但是前面已经提到可以自定义停用词; 可以加中文停词器

代码语言:txt
复制
# 还是前面的表; 建一个ngram全文检索索引,前一个全文索引一定要删掉,不然这个不生效
ALTER TABLE articles ADD FULLTEXT INDEX ft_index (title,body) WITH PARSER ngram;


# 插入数据
INSERT INTO articles (title,body) VALUES
    ('数据库管理','在本教程中我将向你展示如何管理数据库'),
    ('数据库应用开发','学习开发数据库应用程序');


SELECT * FROM INFORMATION_SCHEMA.INNODB_FT_INDEX_CACHE ORDER BY doc_id, position LIMIT 15;

WORD	FIRST_DOC_ID	LAST_DOC_ID	DOC_COUNT	DOC_ID	POSITION
---
数据库管理	9	9	1	9	0
数据	9	10	2	9	0
据库	9	10	2	9	3
库管	9	9	1	9	6
管理	9	9	1	9	9
在本教程中我将向你展示如何管理数据库	9	9	1	9	16
在本	9	9	1	9	16
本教	9	9	1	9	19
教程	9	9	1	9	22
程中	9	9	1	9	25
中我	9	9	1	9	28
我将	9	9	1	9	31
将向	9	9	1	9	34
向你	9	9	1	9	37
你展	9	9	1	9	40


SELECT * FROM articles where MATCH(title,body) AGAINST('数据库应用')

id	title	body
---
8	数据库应用开发	学习开发数据库应用程序
7	数据库管理	在本教程中我将向你展示如何管理数据库

不同的模式有细微的不同, 官网有一个小示例:

  • 如果是 natural language mode 模式; 一个ab文档,一个abc文档,搜 ab bc 都可以搜到
  • 如果是 boolean mode search, 模式; 一个ab文档,一个abc文档, 搜 ab bc 只能搜到abc这一条

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 使用范围及限制
  • 全文检索的全局配置
  • 数据准备
  • 全文检索元数据
    • INNODB_FT_CONFIG
      • INNODB_FT_BEING_DELETED
        • INNODB_FT_DELETED
          • INNODB_FT_DEFAULT_STOPWORD
            • INNODB_FT_INDEX_CACHE
              • INNODB_FT_INDEX_TABLE
              • 全文检索查询
                • 查询模式
                  • 简单查询
                    • 相关度分数查询
                      • 布尔全文检索
                        • 全文检索扩展查询(同义词效果)
                        • 自定义停用词
                        • ngram全文检索器(中文停用词)
                        相关产品与服务
                        云数据库 SQL Server
                        腾讯云数据库 SQL Server (TencentDB for SQL Server)是业界最常用的商用数据库之一,对基于 Windows 架构的应用程序具有完美的支持。TencentDB for SQL Server 拥有微软正版授权,可持续为用户提供最新的功能,避免未授权使用软件的风险。具有即开即用、稳定可靠、安全运行、弹性扩缩等特点。
                        领券
                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档