Mysql实现简易版搜索引擎的示例代码


Posted in MySQL onAugust 30, 2021

前言

前段时间,因为项目需求,需要根据关键词搜索聊天记录,这不就是一个搜索引擎的功能吗?

于是我第一时间想到的就是 ElasticSearch 分布式搜索引擎,但是由于一些原因,公司的服务器资源比较紧张,没有额外的机器去部署一套 ElasticSearch 服务,而且上线时间也比较紧张,数据量也不大,然后就想到了 Mysql 的全文索引。

简介

其实 Mysql 很早就支持全文索引了,只不过一直只支持英文的检索,从5.7.6 版本开始,Mysql 就内置了 ngram 全文解析器,用来支持中文、日文、韩文分词。

Mysql 全文索引采用的是倒排索引的原理,在倒排索引中关键词是主键,每个关键词都对应着一系列文件,这些文件中都出现了这个关键词。这样当用户搜索某个关键词时,排序程序在倒排索引中定位到这个关键词,就可以马上找出所有包含这个关键词的文件。

本文测试,基于 Mysql 8.0 版本,数据库引擎采用的是 InnoDB

ngram 全文解析器

ngram 就是一段文字里面连续的 n 个字的序列。ngram 全文解析器能够对文本进行分词,每个单词是连续的 n 个字的序列。例如,用 ngram 全文解析器对“你好靓仔”进行分词:

 

n=1: '你', '好', '靓', '仔' 
n=2: '你好', '好靓', '靓仔' 
n=3: '你好靓', '好靓仔' 
n=4: '你好靓仔'

MySQL 中使用全局变量 ngram_token_size 来配置 ngram 中 n 的大小,它的取值范围是1到10,默认值是 2。通常 ngram_token_size 设置为要查询的单词的最小字数。如果需要搜索单字,就要把 ngram_token_size 设置为 1。在默认值是 2 的情况下,搜索单字是得不到任何结果的。因为中文单词最少是两个汉字,推荐使用默认值 2。

可以通过以下命令查看 Mysql 默认的 ngram_token_size 大小:

show variables like 'ngram_token_size'

Mysql实现简易版搜索引擎的示例代码

有两种方式可以设置全局变量 ngram_token_size 的值:

1、启动 mysqld 命令时指定:

mysqld --ngram_token_size=2

2、修改 Mysql 配置文件 my.ini,末尾增加一行参数:

ngram_token_size=2

创建全文索引

1、建表时创建全文索引

CREATE TABLE `article` (
  `id` bigint NOT NULL,
  `url` varchar(1024) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
  `title` varchar(256) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
  `source` varchar(32) COLLATE utf8mb4_general_ci DEFAULT '',
  `keywords` varchar(32) COLLATE utf8mb4_general_ci DEFAULT NULL,
  `publish_time` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  FULLTEXT KEY `title_index` (`title`) WITH PARSER `ngram`
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

2、通过 alter table 方式

ALTER TABLE article ADD FULLTEXT INDEX title_index(title) WITH PARSER ngram;

3、通过 create index 方式

CREATE FULLTEXT INDEX title_index ON article (title) WITH PARSER ngram;

检索方式

1、自然语言检索(NATURAL LANGUAGE MODE)

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

示例

select * from article where MATCH(title) AGAINST ('北京旅游' IN NATURAL LANGUAGE MODE);

// 不指定模式,默认使用自然语言模式
select * from article where MATCH(title) AGAINST ('北京旅游');

Mysql实现简易版搜索引擎的示例代码

可以看出,该模式下根据“北京旅游”搜索,可以搜索出包含“北京”的或者包含“旅游”的内容,因为它是根据自然语言分成了两个关键词。

上面示例中返回的结果会自动按照匹配度排序,匹配度高的在前面,匹配度是一个非负浮点数。

示例

// 查看匹配度
select * , MATCH(title) AGAINST ('北京旅游') as score from article where MATCH(title) AGAINST ('北京旅游' IN NATURAL LANGUAGE MODE);

Mysql实现简易版搜索引擎的示例代码

2、布尔检索(BOOLEAN MODE)

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

示例

// 无操作符
// 包含“约会”或“攻略”
select * from article where MATCH(title) AGAINST ('约会 攻略' IN BOOLEAN MODE);

Mysql实现简易版搜索引擎的示例代码

// 使用操作符
// 必须包含“约会”,可包含“攻略”
select * from article where MATCH(title) AGAINST ('+约会 攻略' IN BOOLEAN MODE);

Mysql实现简易版搜索引擎的示例代码

更多操作符示例:

'约会 攻略' 
无操作符,表示或,要么包含“约会”,要么包含“攻略”

'+约会 +攻略'
必须同时包含两个词

'+约会 攻略'
必须包含“约会”,但是如果也包含“攻略”的话,匹配度更高。

'+约会 -攻略'
必须包含“约会”,同时不能包含“攻略”。

'+约会 ~攻略'
必须包含“约会”,但是如果也包含“攻略”的话,匹配度要比不包含“攻略”的记录低。

'+约会 +(>攻略 <技巧)'
查询必须包含“约会”和“攻略”或者“约会”和“技巧”的记录,但是“约会 攻略”的匹配度要比“约会 技巧”高。

'约会*'
查询包含以“约会”开头的记录。

'"约会攻略"'
使用双引号把要搜素的词括起来,效果类似于like '%约会攻略%',
例如“约会攻略初级篇”会被匹配到,而“约会的攻略”就不会被匹配。

与 Like 对比

全文索引和 like 查询对比,有以下优点:

  • like 只是进行模糊匹配,全文索引却提供了一些语法语义的查询功能,会将要查的字符串进行分词操作,这决定于 Mysql 的词库。
  • 全文索引可以自己设置词语的最小、最大长度,要忽略的词,这些都是可以设置的。
  • 用全文索引去某个列查一个字符串,会返回匹配度,可以理解为匹配的关键字个数,是个浮点数。

而且全文检索的性能也是优于 like 查询的

以下是以 50w 左右数据进行的测试:

// like 查询
select * from article where title like '%北京%';

Mysql实现简易版搜索引擎的示例代码

// 全文索引查询
select * from article where MATCH(title) AGAINST ('北京' IN BOOLEAN MODE);

Mysql实现简易版搜索引擎的示例代码

可以看出 like 查询是 1.536s,全文索引查询是 0.094s,快了16倍左右。

总结

全文索引能快速搜索,但是也存在维护索引的开销。字段长度越大,创建的全文索引也越大,会影响DML语句的吞吐量。数据量不大的情况下可以采用全文索引来做搜索,简单方便,但是数据量大的话还是建议用专门的搜索引擎 ElasticSearch 来做这件事。

到此这篇关于Mysql实现简易版搜索引擎的示例代码的文章就介绍到这了,更多相关Mysql 搜索引擎内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

MySQL 相关文章推荐
MySQL pt-slave-restart工具的使用简介
Apr 07 MySQL
详解MySQL 联合查询优化机制
May 10 MySQL
一文读懂navicat for mysql基础知识
May 31 MySQL
mysql 直接拷贝data 目录下文件还原数据的实现
Jul 25 MySQL
sql注入教程之类型以及提交注入
Aug 02 MySQL
Mysql数据库中datetime、bigint、timestamp来表示时间选择,谁来存储时间效率最高
Aug 23 MySQL
MySQL图形化管理工具Navicat安装步骤
Dec 04 MySQL
教你如何让spark sql写mysql的时候支持update操作
Feb 15 MySQL
SQL语句多表联合查询的方法示例
Apr 18 MySQL
jdbc中自带MySQL 连接池实践示例
Jul 23 MySQL
MySQL一劳永逸永久支持输入中文的方法实例
Aug 05 MySQL
mysql数据库如何转移到oracle
Dec 24 MySQL
详细聊聊MySQL中慢SQL优化的方向
Aug 30 #MySQL
MySQL8.0的WITH查询详情
Aug 30 #MySQL
Prometheus 监控MySQL使用grafana展示
Aug 30 #MySQL
MySQL命令无法输入中文问题的解决方式
Aug 30 #MySQL
mysql 索引合并的使用
Aug 30 #MySQL
MySQL去除重叠时间求时间差和的实现
Aug 23 #MySQL
Mysql数据库中datetime、bigint、timestamp来表示时间选择,谁来存储时间效率最高
Aug 23 #MySQL
You might like
php中生成随机密码的自定义函数代码
2013/10/21 PHP
利用PHP fsockopen 模拟POST/GET传送数据的方法
2015/09/22 PHP
PHP内核学习教程之php opcode内核实现
2016/01/27 PHP
PHP浮点比较大小的方法
2016/02/14 PHP
phpcms实现验证码替换及phpcms实现全站搜索功能教程详解
2017/12/13 PHP
js 手机号码合法性验证代码集合
2012/09/29 Javascript
text-align:justify实现文本两端对齐 兼容IE
2015/08/19 Javascript
JavaScript操作XML/HTML比较常用的对象属性集锦
2015/10/30 Javascript
BootStrap.css 在手机端滑动时右侧出现空白的原因及解决办法
2016/06/07 Javascript
第七篇Bootstrap表单布局实例代码详解(三种表单布局)
2016/06/21 Javascript
基于jQuery和Bootstrap框架实现仿知乎前端动态列表效果
2016/11/09 Javascript
Nodejs读取文件时相对路径的正确写法(使用fs模块)
2017/04/27 NodeJs
JavaScript碎片—函数闭包(模拟面向对象)
2019/03/13 Javascript
Vue项目页面跳转时浏览器窗口上方显示进度条功能
2020/03/26 Javascript
Vue微信公众号网页分享的示例代码
2020/05/28 Javascript
使用Vue-cli 中为单独页面设置背景图片铺满全屏
2020/07/17 Javascript
vue实现的多页面项目如何优化打包的步骤详解
2020/07/19 Javascript
Javascript中Math.max和Math.max.apply的区别和用法详解
2020/08/24 Javascript
在Python中操作列表之List.pop()方法的使用
2015/05/21 Python
浅谈Python中用datetime包进行对时间的一些操作
2016/06/23 Python
python模块之time模块(实例讲解)
2017/09/13 Python
python读取excel指定列数据并写入到新的excel方法
2018/07/10 Python
使用python动态生成波形曲线的实现
2019/12/04 Python
Bowflex美国官方网站:高级家庭健身器材
2017/12/22 全球购物
古驰英国官网:GUCCI英国
2020/03/07 全球购物
C#里面如何倒序排列一个数组的元素?
2013/06/21 面试题
JSP和Servlet有哪些相同点和不同点,他们之间的联系是什么?
2015/10/22 面试题
小学生评语集锦
2014/04/18 职场文书
2015年财务个人工作总结范文
2015/05/22 职场文书
新郎结婚感言
2015/07/31 职场文书
初中班主任工作随笔
2015/08/15 职场文书
2016教师读书思廉心得体会
2016/01/23 职场文书
入党转正申请书范文
2019/05/20 职场文书
浅谈Python中的函数(def)及参数传递操作
2021/05/25 Python
Spring Boot 实现敏感词及特殊字符过滤处理
2021/06/29 Java/Android
mysql查看表结构的三种方法总结
2022/07/07 MySQL