文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

mysql ft指的是什么

2023-04-19 05:46

关注

本文小编为大家详细介绍“mysql ft指的是什么”,内容详细,步骤清晰,细节处理妥当,希望这篇“mysql ft指的是什么”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。

mysql ft指的是FullText,即全文索引;全文索引是为了解决需要基于相似度的查询,而不是精确数值比较;全文索引在大量的数据面前,能比like快N倍,速度不是一个数量级。

MySQL 全文索引 (FullText)

一、简介

基本概念

全文索引是为了解决需要基于相似度的查询,而不是精确数值比较。

虽然使用 like + % 也可以实现模糊匹配,但是对于大量的文本数据检索,是不可想象的。全文索引在大量的数据面前,能比 like 快 N 倍,速度不是一个数量级。

版本支持

  1. MySQL 5.6 以前的版本,只有 MyISAM 存储引擎支持全文索引

  2. MySQL 5.6 及以后的版本,MyISAMInnoDB 存储引擎均支持全文索引

  3. MySQL 5.7.6 中,提供了支持中文、日文和韩文(CJK)的内置全文 ngram 解析器,以及用于日文的可安装 MeCab 全文解析器插件

  4. 全文索引只能用于InnoDBMyISAM表,只能为CHARVARCHARTEXT列创建

  5. 对于大型数据集,<span style="" quot="quot" dashed="""dashed""" yellow="""yellow""">将数据加载到没有全文索引的表中然后创建索引要比将数据加载到具有现有全文索引的表中快得多

  6. RDS MySQL 5.6 虽然也支持中文全文检索,但存在BUG

限制与缺点

二、操作全文索引

2.1 配置最小搜索长度

我们可以通过 SQL 命令查看当前配置的最小搜索长度(分词长度):

SHOW VARIABLES LIKE 'ft%';

Variable_nameValue
ft_boolean_syntax+ -><()~*:""&|
ft_max_word_len84
ft_min_word_len1
ft_query_expansion_limit20
ft_stopword_file(built-in)

全文索引的相关参数都无法进行动态修改,必须通过修改 MySQL 的配置文件来完成。修改最小搜索长度的值为 1,首先打开 MySQL 的配置文件 /etc/my.cnf,在 [mysqld] 的下面追加以下内容:

[mysqld]
innodb_ft_min_token_size = 1
# 最短的索引字符串,默认值为4
ft_min_word_len = 1

配置完后重启 MySQL 服务器,并修复或重建全文索引方可生效。
可使用下面的命令修复:

repair table test quick;

2.2 创建索引

CREATE TABLE fulltext_test (
  id int(11) NOT NULL AUTO_INCREMENT,
	content TEXT NOT NULL,
	tag VARCHAR(255),
	PRIMARY KEY (id),
	FULLTEXT KEY content_tag_fulltext(content, tag) WITH PARSER ngram
) ENGINE = InnoDB DEFAULT CHARSET=utf8mb4;

CREATE FULLTEXT INDEX content_fulltext ON fulltext_test(content) with parser ngram;

ALTER TABLE fulltext_test ADD FULLTEXT INDEX content_fulltext(content) with parser ngram;

2.3 删除索引

DROP INDEX content_fulltext ON fulltext_test;

ALTER TABLE fulltext_test DROP INDEX content_fulltext;

三、检索数据

3.1 自然语言的全文检索

默认情况下,或者使用 in natural language mode 修饰符时,match() 函数对文本集合执行自然语言搜索。

SELECT * FROM 表名 WHERE Match(列名1,列名2) Against (检索内容1 检索内容2);

检索内容不需要用逗号隔开!

自然语言搜索引擎将计算每一个文档对象和查询的相关度。这里,相关度是基于匹配的关键词的个数,以及关键词在文档中出现的次数。在整个索引中出现次数越少的词语,匹配时的相关度就越高。相反,非常常见的单词将不会被搜索,如果一个词语的在超过 50% 的记录中都出现了,那么自然语言的搜索将不会搜索这类词语。

3.2 布尔全文检索

在布尔搜索中,我们可以在查询中自定义某个被搜索的词语的相关性,当编写一个布尔搜索查询时,可以通过一些前缀修饰符来定制搜索。

+aaa +(>bbb <ccc) aaa="aaa" sql="sql" select="select" from="from" test="test" where="where" match="match" against="against" in="in" boolean="boolean" mode="mode" select="select" from="from" tommy="tommy" where="where" match="match" against="against" in="in" boolean="boolean" mode="mode" select="select" from="from" tommy="tommy" where="where" match="match" against="against">李秀琴 <练习册 <不是人>是个鬼' in boolean mode);

四、测试结果

测试环境:本机4核16G Windows10,MySQL 8.0
测试数据量salebilldetail1276万行,salebill269 万行, customer30 万行, goods75 万行。

争对测试用的SQL语句,增加了以下全文索引:

CREATE FULLTEXT INDEX billno_fulltext ON salebill(billno) WITH PARSER ngram;
CREATE FULLTEXT INDEX remarks_fulltext ON salebill(remarks) WITH PARSER ngram;
CREATE FULLTEXT INDEX remarks_fulltext ON salebilldetail(remarks) WITH PARSER ngram;
CREATE FULLTEXT INDEX goodsremarks_fulltext ON salebilldetail(goodsremarks) WITH PARSER ngram;
CREATE FULLTEXT INDEX remarks_goodsremarks_fulltext ON salebilldetail(remarks, goodsremarks) WITH PARSER ngram;
CREATE FULLTEXT INDEX custname_fulltext ON customer(custname) WITH PARSER ngram;
CREATE FULLTEXT INDEX goodsname_fulltext ON goods(goodsname) WITH PARSER ngram;
CREATE FULLTEXT INDEX goodscode_fulltext ON goods(goodscode) WITH PARSER ngram;

测试结果,总的来说很魔幻。
为什么魔幻,看下面几个语句:

test_1

-- 测试1,原始 like 查询方式,用时 0.765s
select 1 from salebilldetail d where d.tid=260434 and ((d.remarks like concat('%','葡萄','%')) or (d.goodsremarks like concat('%','葡萄','%')));

test_2

-- 测试2,使用全文索引 remarks_fulltext、goodsremarks_fulltext, 用时 0.834s
select 1 from salebilldetail d where d.tid=260434 and ((match(d.remarks) Against(concat('"','葡萄','"') in boolean mode)) or (match(d.goodsremarks) Against(concat('"','葡萄','"')  in boolean mode)));

test_3

-- 测试3,使用全文索引 remarks_goodsremarks_fulltext, 用时 0.242s
select 1 from salebilldetail d where d.tid=260434 and ((match(d.remarks,d.goodsremarks) Against(concat('"','葡萄','"') in boolean mode)));

test_4

-- 测试4,原始 like 查询方式,不过滤 tid ,用时 22.654s
select t from salebilldetail d where ((d.remarks like concat('%','葡萄','%')) or (d.goodsremarks like concat('%','葡萄','%')));

test_5

-- 测试5,使用全文索引 remarks_fulltext、goodsremarks_fulltext,  不过滤 tid ,用时 24.855s
select 1 from salebilldetail d where ((match(d.remarks) Against(concat('"','葡萄','"') in boolean mode)) or (match(d.goodsremarks) Against(concat('"','葡萄','"')  in boolean mode)));

test_6

-- 测试6,使用全文索引 remarks_goodsremarks_fulltext, 不过滤 tid ,用时 0.213s
select 1 from salebilldetail d where ((match(d.remarks,d.goodsremarks) Against(concat('"','葡萄','"') in boolean mode)));

test_7

-- 测试7,使用全文索引 remarks_goodsremarks_fulltext, 用时 0.22s
select count(1) from salebilldetail d where d.tid=260434 and  ((match(d.remarks,d.goodsremarks) Against(concat('"','葡萄','"') in boolean mode)));

test_8

-- 测试8,使用全文索引 remarks_goodsremarks_fulltext, 不过滤 tid ,用时 0.007s
select count(1) from salebilldetail d where ((match(d.remarks,d.goodsremarks) Against(concat('"','葡萄','"') in boolean mode)));

从上面的测试语句可以看出,数据量越多,查询越简单,全文索引的效果越好

再来看看我们的业务测试SQL:

test_9

-- 测试9
select 
    i.billid
    ,if(0,0,i.qty) as qty  
    ,if(0,0,i.goodstotal) as total          
    ,if(0,0,i.chktotal) as selfchktotal   
    ,if(0,0,i.distotal) as distotal 
    ,if(0,0,i.otherpay) as feetotal  
    ,if(0,0,ifnull(d.costtotal,0)) as costtotal  
    ,if(0,0,ifnull(d.maoli,0)) as maoli         
    ,i.billno
    ,from_unixtime(i.billdate,'%Y-%m-%d') as billdate 
    ,from_unixtime(i.createdate,'%Y-%m-%d %H:%i:%s') as createdate 
    ,if(i.sdate=0,'',from_unixtime(i.sdate,'%Y-%m-%d  %H:%i:%s')) as sdate 
    ,from_unixtime(i.udate,'%Y-%m-%d %H:%i:%s') as udate 
    ,i.custid ,c.custname
    ,i.storeid ,k.storename
    ,i.empid ,e.empname
    ,i.userid ,u.username
    ,i.remarks                               
    ,i.effect,i.settle,i.redold,i.rednew     
    ,i.printtimes 
    ,(case  when i.rednew=1 then 1  when i.redold=1 then 2  when i.settle=1 then 3  when i.effect=1 then 4  else 9 end) as state 
    ,(case  when i.rednew=1 then '红冲单'  when i.redold=1 then '已红冲'  when i.settle=1 then '已结算'  when i.effect=1 then '已过账'  else '草稿' end) as statetext
    ,'' as susername 
    ,'' as accname 
from salebill i
left join coursecentersale d on d.tid=i.tid and d.billid=i.billid
left join customer c on c.tid=i.tid and c.custid=i.custid
left join store k on k.tid=i.tid and k.storeid=i.storeid
left join employee e on e.tid=i.tid and e.empid=i.empid
left join user u on u.tid=i.tid and u.userid=i.userid
where i.tid=260434 and (i.billtype = 5 or i.effect = 1)
    and ('_billdate_f_'!='')
    and ('_billdate_t_'!='')
    and ('_sdate_f_'!='')
    and ('_sdate_t_'!='')
    and ('_udate_f_'!='')
    and ('_udate_t_'!='')
    and ('_cdate_f_'!='')
    and ('_cdate_t_'!='')
    and ('_billid_'!='')      
    and ('_custid_'!='')      
    and ('_storeid_'!='')     
    and ('_empid_'!='')       
    and ('_custstop_'!='')       
    and (
        (i.billno like concat('%','葡萄','%'))
        or (i.remarks like concat('%','葡萄','%'))
        or exists(select 1 from salebilldetail d where d.tid=260434 and d.billid=i.billid and ((d.remarks like concat('%','葡萄','%')) or (d.goodsremarks like concat('%','葡萄','%'))))
        or exists(select 1 from customer c where c.tid=260434 and c.custid=i.custid and (c.custname like concat('%','葡萄','%')))
        or exists(select 1 from goods g join salebilldetail d on d.tid=g.tid and d.goodsid=g.goodsid where d.tid=260434 and d.billid=i.billid and ((g.goodsname like concat('%','葡萄','%')) or (g.goodscode like concat('%','葡萄','%'))))
    )
    and i.rednew=0  
    and i.billid not in (select billid from coursecenter_del t where t.tid=260434)
    and ((i.settle=1 and i.effect=1 and i.redold=0 and i.rednew=0)) 
order by udate desc,billno desc
limit 0,100;

执行时间约 1.6 秒,使用的是 like 方式。

改成使用全文索引方式:

test_10

-- 测试10
select 
    i.billid
    ,if(0,0,i.qty) as qty         
    ,if(0,0,i.goodstotal) as total   
    ,if(0,0,i.chktotal) as selfchktotal  
    ,if(0,0,i.distotal) as distotal 
    ,if(0,0,i.otherpay) as feetotal  
    ,if(0,0,ifnull(d.costtotal,0)) as costtotal 
    ,if(0,0,ifnull(d.maoli,0)) as maoli  
    ,i.billno
    ,from_unixtime(i.billdate,'%Y-%m-%d') as billdate 
    ,from_unixtime(i.createdate,'%Y-%m-%d %H:%i:%s') as createdate 
    ,if(i.sdate=0,'',from_unixtime(i.sdate,'%Y-%m-%d  %H:%i:%s')) as sdate 
    ,from_unixtime(i.udate,'%Y-%m-%d %H:%i:%s') as udate 
    ,i.custid ,c.custname
    ,i.storeid ,k.storename
    ,i.empid ,e.empname
    ,i.userid ,u.username
    ,i.remarks                               
    ,i.effect,i.settle,i.redold,i.rednew     
    ,i.printtimes 
    ,(case  when i.rednew=1 then 1  when i.redold=1 then 2  when i.settle=1 then 3  when i.effect=1 then 4  else 9 end) as state 
    ,(case  when i.rednew=1 then '红冲单'  when i.redold=1 then '已红冲'  when i.settle=1 then '已结算'  when i.effect=1 then '已过账'  else '草稿' end) as statetext
    ,'' as susername 
    ,'' as accname 
from salebill i
left join coursecentersale d on d.tid=i.tid and d.billid=i.billid
left join customer c on c.tid=i.tid and c.custid=i.custid
left join store k on k.tid=i.tid and k.storeid=i.storeid
left join employee e on e.tid=i.tid and e.empid=i.empid
left join user u on u.tid=i.tid and u.userid=i.userid
where i.tid=260434 and (i.billtype = 5 or i.effect = 1)
    and ('_billdate_f_'!='')
    and ('_billdate_t_'!='')
    and ('_sdate_f_'!='')
    and ('_sdate_t_'!='')
    and ('_udate_f_'!='')
    and ('_udate_t_'!='')
    and ('_cdate_f_'!='')
    and ('_cdate_t_'!='')
    and ('_billid_'!='')      
    and ('_custid_'!='')      
    and ('_storeid_'!='')     
    and ('_empid_'!='')       
    and ('_custstop_'!='')       
    and (
        (match(i.billno) against(concat('"','葡萄','"') in boolean mode))
        or (match(i.remarks) against(concat('"','葡萄','"') in boolean mode))
        or exists(select 1 from salebilldetail d where d.tid=260434 and d.billid=i.billid and ((match(d.remarks) Against(concat('"','葡萄','"') in boolean mode)) or (match(d.goodsremarks) Against(concat('"','葡萄','"')  in boolean mode))))
        or exists(select 1 from customer c where c.tid=260434 and c.custid=i.custid and (match(c.custname) Against(concat('"','葡萄','"') in boolean mode)))
        or exists(select 1 from goods g join salebilldetail d on d.tid=g.tid and d.goodsid=g.goodsid where d.tid=260434 and d.billid=i.billid 
     and ((match(g.goodsname) Against(concat('"','葡萄','"') in boolean mode))
     or (match(g.goodscode) Against(concat('"','葡萄','"') in boolean mode))))
    )
    and i.rednew=0  
    and i.billid not in (select billid from coursecenter_del t where t.tid=260434)
    and ((i.settle=1 and i.effect=1 and i.redold=0 and i.rednew=0)) 
order by udate desc,billno desc
limit 0,100;

执行时间约 1.6 秒,与使用的是 like 方式差不多。

最魔幻的地方来了,如果将上面的SQL语句中(salebilldetail表使用全文索引 remarks_fulltextgoodsremarks_fulltext的地方)

exists(select 1 from salebilldetail d where d.tid=260434 and d.billid=i.billid and ((match(d.remarks) Against(concat('"','葡萄','"') in boolean mode)) or (match(d.goodsremarks) Against(concat('"','葡萄','"')  in boolean mode))))

test_11

改成使用全文索引 remarks_goodsremarks_fulltext

-- 测试11
exists(select 1 from salebilldetail d where d.tid=260434 and d.billid=i.billid and ((match(d.remarks,d.goodsremarks) Against(concat('"','葡萄','"') in boolean mode))))

执行时间无限长(跑了半天没成功)?
经分析,在 where 子句中,一个条件子句中包含一个以上 match 时会出现这样的情况。即:

-- and 中只有一个全文检索时正常, 用时0.2秒
select xxx from xxx
...
and (
	exists(select 1 from salebilldetail d where d.tid=260434 and d.billid=i.billid and ((match(d.remarks,d.goodsremarks) Against(concat('"','葡萄','"') in boolean mode))))
)
...

-- 下面这样就异常了,会慢成百上千倍,用时 160 秒, 如果有更多的 match ,会更夸张的慢下去
select xxx from xxx
...
and (
	exists(select 1 from salebilldetail d where d.tid=260434 and d.billid=i.billid and ((match(d.remarks,d.goodsremarks) Against(concat('"','葡萄','"') in boolean mode))))
	or match(i.billno) against(concat('"','葡萄','"') in boolean mode)
)
...

测试结果汇总

查询用时(秒)备注
test 10.765原始like查询
test 20.834全文索引 remarks_fulltextgoodsremarks_fulltext
test 30.242全文索引 remarks_goodsremarks_fulltext
---

test 422.654原始like查询,不过滤 tid
test 524.855全文索引 remarks_fulltextgoodsremarks_fulltext,  不过滤 tid
test 60.213全文索引 remarks_goodsremarks_fulltext, 不过滤 tid
---

test 70.22全文索引 remarks_goodsremarks_fulltext, count
test 80.007全文索引 remarks_goodsremarks_fulltext, 不过滤 tid, count
---

test 91.6业务测试SQL,原始like查询
test 101.6业务测试SQL,全文索引 remarks_fulltextgoodsremarks_fulltext
test 11失败业务测试SQL,全文索引 remarks_goodsremarks_fulltext

五、MySQL 版本升级

因线上系统目前是 RDS MySQL 5.6,故简单描述升级相关问题。

读到这里,这篇“mysql ft指的是什么”文章已经介绍完毕,想要掌握这篇文章的知识点还需要大家自己动手实践使用过才能领会,如果想了解更多相关内容的文章,欢迎关注亿速云行业资讯频道。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     801人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     348人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     311人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     432人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     220人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-数据库
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯