目录
需求
全文索引介绍
1、发展历史
2、全文索引
3、创建全文索引
4、全文索引使用
中文分词与全文索引
什么是N-gram?
这个上面这个N是怎么去配置的?
查一下目前的值
修改方式
实际使用
初始化测试数据
添加索引
查询
1、使用自然语言模式 NATURAL LANGUAGE MODE 查询
2、使用布尔模式(BOOLEAN MODE)查询
注意点
首页 数据库 mysql教程 聊聊MySQL全文索引怎么解决like模糊匹配查询慢

聊聊MySQL全文索引怎么解决like模糊匹配查询慢

Oct 31, 2022 pm 08:03 PM
mysql

聊聊MySQL全文索引怎么解决like模糊匹配查询慢

模糊查询,如查询姓名包含”晓“的用户,常见的写法为 like "%晓%",MySQL里面他会全表扫描,数据量少还好,全表扫描也很快,随着数据增加会变慢,上ES又很重。本篇文章就来给大家介绍like模糊匹配查询慢解决之道——MySQL全文索引。

需求

需要模糊匹配查询一个单词

select * from t_phrase where LOCATE('昌',phrase) = 0;

select * from t_chinese_phrase where instr(phrase,'昌') > 0;

select * from t_chinese_phrase where phrase like '%昌%'

explain一下看看执行计划

1.png

由explain的结果可知,虽然我们给phrase建了索引,但是查询的时候,索引是失效的。

原因: mysql的索引是B+树结构,InnoDB在模糊查询数据时使用 "%xx" 会导致索引失效(此处就不展开讲了)

从查询时长上来看,花费时间:90ms

目前数据量:93230(9.3W)已经需要90ms,这个时间不太能接受,假如数据量增加,这个时间会不断增长。

解决方案:

数据量不大的情况下,使用mysql的全文索引;
数据量比较大或者mysql的全文索引不达预期的情况下,可以考虑使用ES

下面主要是MySQL的全文索引相关.

全文索引介绍

1、发展历史

  • 旧版的MySQL的全文索引只能用在MyISAM存储引擎的char、varchar和text的字段上。

  • MySQL5.6.24上InnoDB引擎也加入了全文索引。

2、全文索引

  • 全文检索(Full-Text Search) 是将存储于数据库中的整本书或整篇文章中的任意内容信息查找出来的技术。它可以根据需要获得全文中有关章、节、段、词等信息,也可以进行各种统计和分析

3、创建全文索引

若需对大量数据设置全文索引,建议先添加数据再创建索引。

1、创建表时创建全文索引

create table 表名(
字段名1,
字段名2,
字段名3,
字段名4,
FULLTEXT full_index_name (字段名)
)ENGINE=InnoDB;
登录后复制

2、为已有表添加全文索引

create fulltext index 索引名称 on 表名(字段名);

eg:

create table t_word
(
    id        int unsigned auto_increment comment '自增id' primary key,
    uid       char(32)     not null comment '32位唯一id',
    word      varchar(256) null comment '英文单词',
    translate varchar(256) null
);

create fulltext index full_idx_translate
    on t_word (translate);

create fulltext index full_idx_word
    on t_word (word);

INSERT INTO t_word (id, uid, word, translate) VALUES (1, '9d592499c65648b0a9519206688ef3f9', 'lion', '狮子');
INSERT INTO t_word (id, uid, word, translate) VALUES (2, 'ce26ac4239514bc6af481bcb1d9b67df', 'panda', '熊猫');
INSERT INTO t_word (id, uid, word, translate) VALUES (3, 'a7d6042853c44904b68275daafb44702', 'tiger', '老虎');
INSERT INTO t_word (id, uid, word, translate) VALUES (4, 'f13bd0a8ecea44fc9ade1625eeb4cc3c', 'goat', '山羊');
INSERT INTO t_word (id, uid, word, translate) VALUES (5, '27d5cbfc93a046388d712085e567474f', 'sheep', '绵羊');
INSERT INTO t_word (id, uid, word, translate) VALUES (6, 'ed35df138cf348aa937781be8ee21cbf', 'lamb', '羊羔');
INSERT INTO t_word (id, uid, word, translate) VALUES (7, 'fba5861d9527440990276e999f47ef8f', 'buffalo', '水牛');
INSERT INTO t_word (id, uid, word, translate) VALUES (8, '3a72e76f210841b1939fff0d3d721375', 'bull', '公牛');
INSERT INTO t_word (id, uid, word, translate) VALUES (9, '272e0b28ea7a48248a86f17533bf9943', 'cow', '母牛');
INSERT INTO t_word (id, uid, word, translate) VALUES (10, '47127adface54e418e4c1b9980af6d16', 'calf', '小牛');
INSERT INTO t_word (id, uid, word, translate) VALUES (11, '10592499c65648b0a9519206688ef3f9', 'little lion', '小狮子');
INSERT INTO t_word (id, uid, word, translate) VALUES (12, '1bf095110b634a01bee5b31c5ee7ee0c', 'little cow', '母牛');
INSERT INTO t_word (id, uid, word, translate) VALUES (13, '4813e588cde54c30bd65bfdbb243ad1f', 'little calf', '小小牛');
INSERT INTO t_word (id, uid, word, translate) VALUES (14, '5e377e281ad344048b6938a638b78ccb', 'little bull', '小公牛');
INSERT INTO t_word (id, uid, word, translate) VALUES (15, '2855ad0da2964c7682c178eb8271f13d', 'little buffalo', '小水牛');
INSERT INTO t_word (id, uid, word, translate) VALUES (16, '72f24c9a77644d57a36f3bdf2b8116b0', 'little lamb', '小羊羔');
INSERT INTO t_word (id, uid, word, translate) VALUES (17, '2d592499c65648b0a9519206688ef3f9', 'I''m a big lion', '我是一只大狮子');
登录后复制

3、删除全文索引

alter table 表名 drop index 索引名;

4、全文索引使用

语法

MATCH(col1,col2,...) AGAINST(expr[search_modifier])
search_modifier:
{
    IN NATURAL LANGUAGE MODE
    | IN NATURAL LANGUAGE MODE WITH QUERY EXPANSION
    | IN BOOLEAN MODE
    | WITH QUERY EXPANSION
}
登录后复制

4.1 IN NATURAL LANGUAGE MODE

自然语言模式是MySQL 默认 的全文检索模式。自然语言模式不能使用操作符,不能指定关键词必须出现或者必须不能出现等复杂查询。

// 默认是使用 in natural language mode
select * from t_word where match(word) against ('lion');
// 或者 显示写
select * from t_word where match(word) against ('lion' in natural language mode);
登录后复制
登录后复制

结果如下:

2.png

4.2 IN BOOLEAN MODE

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

操作者描述
为空默认,包含该词
+包括,这个词必须存在。
-排除,词不得出现。
>(大于号)包括,并提高排名值,查询的结果会靠前
<包括,并降低排名值,查询的结果会靠后
()将单词分组为子表达式(允许将它们作为一组包括在内,排除在外,排名等等)。
否定单词的排名值。
*通配符在这个词的结尾。
“”定义短语(与单个单词列表相对,整个短语匹配以包含或排除)。

示例:

// 默认是使用 in natural language mode
select * from t_word where match(word) against (&#39;lion&#39;);
// 或者 显示写
select * from t_word where match(word) against (&#39;lion&#39; in natural language mode);
登录后复制
登录后复制

3.png

// 排除包含lion记录、查询出包含cow或者little的记录,提升包含calf单词的排名,降低包含cow记录的排名,查询出以go开头的记录
select * from t_word where match(word) against (&#39;-lion cow little >calf <cow  go*&#39; in boolean mode) ;
登录后复制

4.png

好像问题都解决了, 但是问题才刚开始


回到最开始的需求,我想模糊搜索

select * from t_word where  match(word) against(&#39;lio&#39; in boolean mode);
登录后复制

预期值:把包含lion的都查询出来 实际结果:啥都没有。

5.png

全匹配查询的时候能查询出来

select * from t_word where  match(translate) against(&#39;小水牛&#39; in boolean mode);
登录后复制

6.png

只查询部分查询不出来。如:下面只查询 "小水" 或者"水牛" 都没有数据

select * from t_word where  match(translate) against(&#39;小水&#39; in boolean mode);
登录后复制

7.png

奇怪了,这咋没出来呢?

全文索引默认是只按照空格进行分词的,所以当我完整的单个单词去查询的时候是能查出来的。但是使用部分单词去查询或者使用部分中文去查询时,是查询不出来数据的,像中文需要使用中文分词器进行分词。

中文分词与全文索引

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

什么是N-gram?

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

N=1 : &#39;齿&#39;, &#39;轮&#39;, &#39;传&#39;, &#39;动&#39;;
N=2 : &#39;齿轮&#39;, &#39;轮传&#39;, &#39;传动&#39;;
N=3 : &#39;齿轮传&#39;, &#39;轮传动&#39;;
N=4 : &#39;齿轮传动&#39;;
登录后复制

这个上面这个N是怎么去配置的?

查一下目前的值

show variables like &#39;%token%&#39;;
登录后复制

8.png

参数解析:

innodb_ft_min_token_size
默认3,表示最小3个字符作为一个关键词,增大该值可减少全文索引的大小
innodb_ft_max_token_size
默认84,表示最大84个字符作为一个关键词,限制该值可减少全文索引的大小
ngram_token_size
默认2,表示2个字符作为内置分词解析器的一个关键词,合法取值范围是1-10,如对“abcd”建立全文索引,关键词为’ab’,‘bc’,‘cd’ 当使用ngram分词解析器时,innodb_ft_min_token_size和innodb_ft_max_token_size 无效

修改方式

方式1: 在my.cnf中修改/添加参数

[mysqld]ngram_token_size = 1
登录后复制

方式2: 修改启动参数

mysqld --ngram_token_size=1复制代码
登录后复制

参数均不可动态修改,修改后需重启MySQL服务,并重新建立全文索引

实际使用

初始化测试数据

这里只提供部分测试数据,我下面sql使用全量数据,数据对不上

create table t_chinese_phrase
(
    id     int unsigned auto_increment comment &#39;id&#39;
        primary key,
    phrase varchar(32) not null comment &#39;词组&#39;
)
    collate = utf8mb4_general_ci;

INSERT INTO t_chinese_phrase (id, phrase) VALUES (278911, &#39;阿昌族&#39;);
INSERT INTO t_chinese_phrase (id, phrase) VALUES (279253, &#39;八一南昌起义&#39;);
INSERT INTO t_chinese_phrase (id, phrase) VALUES (282316, &#39;昌明&#39;);
INSERT INTO t_chinese_phrase (id, phrase) VALUES (282317, &#39;昌盛&#39;);
INSERT INTO t_chinese_phrase (id, phrase) VALUES (282318, &#39;昌言&#39;);
INSERT INTO t_chinese_phrase (id, phrase) VALUES (286534, &#39;东昌纸&#39;);
INSERT INTO t_chinese_phrase (id, phrase) VALUES (291525, &#39;海昌蓝&#39;);
INSERT INTO test.t_chinese_phrase (id, phrase) VALUES (346682, &#39;繁荣昌盛&#39;);
INSERT INTO test.t_chinese_phrase (id, phrase) VALUES (282317, &#39;昌盛&#39;);
INSERT INTO test.t_chinese_phrase (id, phrase) VALUES (287738, &#39;繁盛&#39;);
INSERT INTO test.t_chinese_phrase (id, phrase) VALUES (287736, &#39;繁荣&#39;);
登录后复制

添加索引

mysql 全文索引使用倒排索引为 full inverted index
结构:{单词,(单词所在文档的ID,单词在具体文件中的位置)}

添加索引:

alter  table t_chinese_phrase add fulltext ful_phrase (phrase) with parser ngram;
登录后复制

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

# test: 库名  t_chinese_phrase: 表名字
SET GLOBAL innodb_ft_aux_table="test/t_chinese_phrase";
# 查询分词情况
SELECT * FROM INFORMATION_SCHEMA.INNODB_FT_INDEX_CACHE;
# 查询分词情况
select * from information_schema.innodb_ft_index_table;
登录后复制

查询结果如下:

9.png

因为我们上面设置了分词数是1,所以,可以看到都是按照一个词进行分词的。

字段解析:
FIRST_DOC_ID :word第一次出现的文档ID
LAST_DOC_ID : word最后一次出现的文档ID
DOC_COUNT :含有word的文档个数
DOC_ID :当前文档ID
POSITION : word 当在前文档ID的位置

查询

1、使用自然语言模式 NATURAL LANGUAGE MODE 查询

在自然语言模式(NATURAL LANGUAGE MODE)下,文本的查询被转换为n-gram分词查询的并集

例如,当ngram_token_size = 1 时,(‘繁荣昌盛’)转换为(‘繁 荣 昌 盛’)。下面一个例子:

SELECT * FROM t_chinese_phrase WHERE MATCH (phrase) AGAINST (&#39;繁荣昌盛&#39; in natural language mode) ;
登录后复制

10.png

2、使用布尔模式(BOOLEAN MODE)查询

布尔模式(BOOLEAN MODE)文本查询被转化为n-gram分词的短语查询

例如,当ngram_token_size = 1 时,(‘繁荣昌盛’)转换为(‘”繁荣昌盛“’)。下面一个例子:

SELECT * FROM t_chinese_phrase WHERE MATCH (phrase) AGAINST (&#39;繁荣昌盛&#39; in boolean  mode) ;
登录后复制

11.png

实际使用

回到我们最开始的查询需求,看看实际的效果

查询包含了“昌”的数据

SELECT * FROM t_chinese_phrase WHERE MATCH (phrase) AGAINST (&#39;昌&#39; IN boolean  MODE) ;
SELECT * FROM t_chinese_phrase WHERE MATCH (phrase) AGAINST (&#39;昌&#39; ) order by id asc;
登录后复制

12.png

可以看到结果:目前“昌”在任意位置都能被查询到。

查询执行计划如下:

13.png

耗时31ms(不走索引是90ms),耗时差不多是之前的1/3

注意点

1、自然语言全文索引创建索引时的字段需与查询的字段保持一致,即MATCH里的字段必须和FULLTEXT里的一模一样;

2、自然语言检索时,检索的关键字在所有数据中不能超过50%(即常见词),则不会检索出结果。可以通过布尔检索查询;

3、在mysql的stopword中的单词检索不出结果。可通过

SELECT * FROM INFORMATION_SCHEMA.INNODB_FT_DEFAULT_STOPWORD
登录后复制

查询所有的stopword。遇到这种情况,有两种解决办法:

(1)stopword一般是mysql自建的,但可以通过设置ft_stopword_file变量为自定义文件,从而自己设置stopword,设置完成后需要重新创建索引。但不建议使用这种方法;

(2)使用布尔索引查询

4、小于最短长度和大于最长长度的关键词无法查出结果。可以通过设置对应的变量来改变长度限制,修改后需要重新创建索引。

myisam引擎下对应的变量名为ft_min_word_len和ft_max_word_len

innodb引擎下对应的变量名为innodb_ft_min_token_size和innodb_ft_max_token_size

5、MySQL5.7.6之前的版本不支持中文,需使用第三方插件

6、全文索引只能在 InnoDB(MySQL 5.6以后) 或 MyISAM 的表上使用,并且只能用于创建 char,varchar,text 类型的列。

【相关推荐:mysql视频教程

以上是聊聊MySQL全文索引怎么解决like模糊匹配查询慢的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
1 个月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
1 个月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
1 个月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.聊天命令以及如何使用它们
1 个月前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

mysql:简单的概念,用于轻松学习 mysql:简单的概念,用于轻松学习 Apr 10, 2025 am 09:29 AM

MySQL是一个开源的关系型数据库管理系统。1)创建数据库和表:使用CREATEDATABASE和CREATETABLE命令。2)基本操作:INSERT、UPDATE、DELETE和SELECT。3)高级操作:JOIN、子查询和事务处理。4)调试技巧:检查语法、数据类型和权限。5)优化建议:使用索引、避免SELECT*和使用事务。

phpmyadmin怎么打开 phpmyadmin怎么打开 Apr 10, 2025 pm 10:51 PM

可以通过以下步骤打开 phpMyAdmin:1. 登录网站控制面板;2. 找到并点击 phpMyAdmin 图标;3. 输入 MySQL 凭据;4. 点击 "登录"。

navicat premium怎么创建 navicat premium怎么创建 Apr 09, 2025 am 07:09 AM

使用 Navicat Premium 创建数据库:连接到数据库服务器并输入连接参数。右键单击服务器并选择“创建数据库”。输入新数据库的名称和指定字符集和排序规则。连接到新数据库并在“对象浏览器”中创建表。右键单击表并选择“插入数据”来插入数据。

MySQL:世界上最受欢迎的数据库的简介 MySQL:世界上最受欢迎的数据库的简介 Apr 12, 2025 am 12:18 AM

MySQL是一种开源的关系型数据库管理系统,主要用于快速、可靠地存储和检索数据。其工作原理包括客户端请求、查询解析、执行查询和返回结果。使用示例包括创建表、插入和查询数据,以及高级功能如JOIN操作。常见错误涉及SQL语法、数据类型和权限问题,优化建议包括使用索引、优化查询和分表分区。

为什么要使用mysql?利益和优势 为什么要使用mysql?利益和优势 Apr 12, 2025 am 12:17 AM

选择MySQL的原因是其性能、可靠性、易用性和社区支持。1.MySQL提供高效的数据存储和检索功能,支持多种数据类型和高级查询操作。2.采用客户端-服务器架构和多种存储引擎,支持事务和查询优化。3.易于使用,支持多种操作系统和编程语言。4.拥有强大的社区支持,提供丰富的资源和解决方案。

navicat怎么新建连接mysql navicat怎么新建连接mysql Apr 09, 2025 am 07:21 AM

可在 Navicat 中通过以下步骤新建 MySQL 连接:打开应用程序并选择“新建连接”(Ctrl N)。选择“MySQL”作为连接类型。输入主机名/IP 地址、端口、用户名和密码。(可选)配置高级选项。保存连接并输入连接名称。

redis怎么使用单线程 redis怎么使用单线程 Apr 10, 2025 pm 07:12 PM

Redis 使用单线程架构,以提供高性能、简单性和一致性。它利用 I/O 多路复用、事件循环、非阻塞 I/O 和共享内存来提高并发性,但同时存在并发性受限、单点故障和不适合写密集型工作负载的局限性。

MySQL和SQL:开发人员的基本技能 MySQL和SQL:开发人员的基本技能 Apr 10, 2025 am 09:30 AM

MySQL和SQL是开发者必备技能。1.MySQL是开源的关系型数据库管理系统,SQL是用于管理和操作数据库的标准语言。2.MySQL通过高效的数据存储和检索功能支持多种存储引擎,SQL通过简单语句完成复杂数据操作。3.使用示例包括基本查询和高级查询,如按条件过滤和排序。4.常见错误包括语法错误和性能问题,可通过检查SQL语句和使用EXPLAIN命令优化。5.性能优化技巧包括使用索引、避免全表扫描、优化JOIN操作和提升代码可读性。

See all articles