当前位置:

首页 > 编程开发 > MySQL全文索引如何优化模糊查询速度问题

MySQL全文索引如何优化模糊查询速度问题

需求需要模糊匹配查询一个单词select*fromt_phrasewhereLOCATE('昌',phrase)=0;select*fromt_chinese_phrasewhereinstr(phrase,'昌')>0;select*fromt_chinese_phrasewherephraselike'%昌%'explain一下看看执行计划由explain的结果可知,虽然我们给phrase建了索引,但是查询的时候,索引是失效的。原因:mysql的索引是B

需求

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

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一下看看执行计划

MySQL全文索引如何优化模糊查询速度问题

由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);

结果如下:

MySQL全文索引如何优化模糊查询速度问题

4.2 IN BOOLEAN MODE

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

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

示例:

// 默认是使用 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);

MySQL全文索引如何优化模糊查询速度问题

// 排除包含lion记录、查询出包含cow或者little的记录,提升包含calf单词的排名,降低包含cow记录的排名,查询出以go开头的记录
select * from t_word where match(word) against ('-lion cow little >calf 

MySQL全文索引如何优化模糊查询速度问题

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


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

select * from t_word where  match(word) against('lio' in boolean mode);

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

MySQL全文索引如何优化模糊查询速度问题

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

select * from t_word where  match(translate) against('小水牛' in boolean mode);

MySQL全文索引如何优化模糊查询速度问题

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

select * from t_word where  match(translate) against('小水' in boolean mode);

MySQL全文索引如何优化模糊查询速度问题

奇怪了,这咋没出来呢?

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

中文分词与全文索引

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

什么是N-gram?

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

N=1 : '齿', '轮', '传', '动';
N=2 : '齿轮', '轮传', '传动';
N=3 : '齿轮传', '轮传动';
N=4 : '齿轮传动';

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

查一下目前的值

show variables like '%token%';

MySQL全文索引如何优化模糊查询速度问题

参数解析:

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 'id'
        primary key,
    phrase varchar(32) not null comment '词组'
)
    collate = utf8mb4_general_ci;

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

添加索引

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;

查询结果如下:

MySQL全文索引如何优化模糊查询速度问题

因为我们上面设置了分词数是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 ('繁荣昌盛' in natural language mode) ;

MySQL全文索引如何优化模糊查询速度问题

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

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

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

SELECT * FROM t_chinese_phrase WHERE MATCH (phrase) AGAINST ('繁荣昌盛' in boolean  mode) ;

MySQL全文索引如何优化模糊查询速度问题

实际使用

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

查询包含了“昌”的数据

SELECT * FROM t_chinese_phrase WHERE MATCH (phrase) AGAINST ('昌' IN boolean  MODE) ;
SELECT * FROM t_chinese_phrase WHERE MATCH (phrase) AGAINST ('昌' ) order by id asc;

MySQL全文索引如何优化模糊查询速度问题

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

查询执行计划如下:

MySQL全文索引如何优化模糊查询速度问题

耗时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 类型的列。

本文内容来源于互联网,如有侵权请联系删除。
作者最新文章
编程开发
相关文章 更多
using namespace 使用中遇到的问题怎么解决
using namespace 使用中遇到的问题怎么解决

命名空间的基本概念与常见引入问题在C++等编程语言中,命名空间(namespace)是一种将代码标识符(如变量、函数、类名)封装在特定名称下的机制,其主要目的是避免命名冲突,尤其是在大型项目或使用多个第三方库时。使用“using namespace”指令可以将指定命名空间中的所有名称引入当前作用域,

c语言函数递归 实操经验总结:这些技巧很实用
c语言函数递归 实操经验总结:这些技巧很实用

理解递归的基本原理在C语言中,递归是一种函数调用自身的编程技术。要掌握它,首先需要理解其核心思想:将一个复杂的大问题,分解为一个或几个与原问题相似但规模更小的子问题,直到子问题足够简单,可以直接求解。这个过程通常包含两个关键部分:递归出口和递归体。递归出口定义了问题何时不再继续分解,即最简单、可直接

c语言函数递归 怎么选?常见方案对比分析
c语言函数递归 怎么选?常见方案对比分析

递归函数的基本概念与适用场景在C语言编程中,递归是一种函数调用自身的编程技巧。它并非适用于所有问题,但在处理某些具有自相似结构的问题时,能提供极其清晰和优雅的解决方案。递归的核心思想是将一个大规模问题分解为一个或多个同类型但规模更小的子问题,直到子问题简单到可以直接求解。典型的适用场景包括树形结构的

Objective-C 内存管理入门:从 alloc 到 dealloc 的生命周期详解
Objective-C 内存管理入门:从 alloc 到 dealloc 的生命周期详解

理解内存管理的基石在Objective-C的编程世界中,内存管理是开发者必须掌握的核心技能之一。它直接关系到应用的性能、稳定性与资源利用效率。与一些采用自动垃圾回收机制的语言不同,Objective-C在很长一段时间里,依赖一套基于引用计数的、需要开发者部分介入的管理规则。这套规则的核心思想是明确的

如何正确使用 dealloc 以避免 iOS 应用中的内存泄漏
如何正确使用 dealloc 以避免 iOS 应用中的内存泄漏

理解 dealloc 的角色与时机在 iOS 应用开发中,内存管理是保障应用性能与稳定性的基石。dealloc 方法是 Objective-C 中对象生命周期结束时的关键回调,它标志着对象即将被系统回收内存。正确理解其触发时机至关重要:当一个对象的引用计数降为零时,运行时系统会自动调用该对象的 de

深入理解 Objective-C 中的 dealloc 方法:内存管理核心机制
深入理解 Objective-C 中的 dealloc 方法:内存管理核心机制

内存管理的基石在Objective-C的世界里,内存管理是开发者必须掌握的核心技能之一。作为一门在手动引用计数(MRC)时代诞生的语言,Objective-C要求程序员对对象的生命周期有清晰的认识。dealloc方法正是这一生命周期中至关重要的终点站。它是一个实例方法,当对象的引用计数降为零时,系统

理解 native2ascii:Java 国际化开发中的字符编码工具
理解 native2ascii:Java 国际化开发中的字符编码工具

native2ascii 工具的基本定位在Ja va应用程序的国际化与本地化开发过程中,处理非拉丁字符集是一个常见且关键的环节。Ja va内部使用Unicode字符集来统一表示全球各种语言的文字,但其属性文件(.properties)在历史上要求使用ASCII编码,或者更准确地说,要求非ASCII字

如何使用 native2ascii 转换中文字符为 Unicode 转义序列
如何使用 native2ascii 转换中文字符为 Unicode 转义序列

理解 native2ascii 工具的基本用途在软件开发,特别是涉及国际化处理的场景中,开发者常常需要处理不同编码的文本资源。native2ascii 是 Ja va 开发工具包(JDK)中提供的一个命令行实用程序,其主要功能是将包含本地字符编码(非ASCII字符)的文件,转换为包含 Unicode

Java native2ascii 命令详解:解决属性文件乱码问题
Java native2ascii 命令详解:解决属性文件乱码问题

native2ascii 命令的由来与作用在Ja va开发中,处理国际化资源文件是一个常见需求。资源文件通常以.properties格式存储,用于支持多语言界面。然而,Ja va属性文件默认采用ISO-8859-1字符集编码,这导致了一个直接的问题:当文件中包含非拉丁字符(如中文、日文、韩文等)时,

一个 memwatch 实战案例:定位野指针问题
一个 memwatch 实战案例:定位野指针问题

内存监控工具的价值与挑战在软件开发,尤其是使用C/C++这类手动管理内存的语言时,内存错误是程序员最常遭遇的难题之一。其中,野指针问题因其隐蔽性和破坏性,往往成为最难定位的“幽灵”缺陷。它可能潜伏在代码中,在特定条件下才被触发,导致程序崩溃、数据损坏或难以预测的行为。传统的调试手段,如打印日志或使用

查看更多
精品专题 更多
装机必备
装机必备

正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

Windows
Windows

正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

macOS软件
macOS软件

正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

Mac软件 更多
灵活计算器
灵活计算器
macOS/iOS/Android

灵活计算器是一款笔记式算数应用,支持实时计算、动态关联和云端同步功能。记录、整理和输出之间的过渡会更自然,适合长期写作、做笔记或持续沉淀个人内容。

赤友清理大师
赤友清理大师
macOS

赤友清理大师是一款为 Mac 设计的智能清理优化工具,可精准扫描垃圾、大文件、重复文件等,释放磁盘空间。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

极度公式
极度公式
Windows/macOS/Linux

极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

WINDOWS 更多
Windows 10
Windows 10
Windows

Windows 10 是一款微软推出的经典操作系统,拥有硬件兼容性与多任务处理能力。它更偏向把系统状态查看和常用调节动作放在一起,适合需要持续观察和微调设备状态的场景。

极度公式
极度公式
Windows/macOS/Linux

极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

密码键盘
密码键盘
Windows/macOS/iOS/Android

密码键盘是一款兼具安全性与便捷性的高效密码管理器。日常使用里的持续防护和信息管理会更突出,适合把安全控制放进长期使用流程中的场景。