1.创建全文索引(FullText index)
旧版的MySQL的全文索引只能用在MyISAM表格的char、varchar和text的字段上。
不过新版的MySQL5.6.24上InnoDB引擎也加入了全文索引,所以具体信息要随时关注官网,
1.1. 创建表的同时创建全文索引
CREATE TABLE article (
id INT AUTO_INCREMENT NOT NULL PRIMARY KEY,
title VARCHAR(200),
body TEXT,
FULLTEXT(title, body)
) TYPE=MYISAM;
1.2.通过 alter table 的方式来添加
ALTER TABLE student
ADD FULLTEXT INDEX ft_stu_name (name
) #ft_stu_name是索引名,可以随便起
或者:ALTER TABLE `student` ADD FULLTEXT ft_stu_name (`name`)
1.3. 直接通过create index的方式
CREATE FULLTEXT INDEX ft_email_name ON `student` (`name`)
也可以在创建索引的时候指定索引的长度:
CREATE FULLTEXT INDEX ft_email_name ON `student` (`name`(20))
2.1. 直接使用 drop index(注意:没有 drop fulltext index 这种用法)
DROP INDEX full_idx_name ON tommy.girl ;
2.2. 使用 alter table的方式
ALTER TABLE tommy.girl DROP INDEX ft_email_abcd;
3.使用全文索引
跟普通索引稍有不同
使用全文索引的格式: MATCH (columnName) AGAINST ('string')
eg:
SELECT * FROM `student` WHERE MATCH(`name`) AGAINST('聪')
当查询多列数据时:
建议在此多列数据上创建一个联合的全文索引,否则使用不了索引的。
SELECT * FROM `student` WHERE MATCH(`name`,`address`) AGAINST('聪 广东')
3.1. 使用全文索引需要注意的是:(基本单位是词)
分词,全文索引以词为基础的,MySQL默认的分词是所有非字母和数字的特殊符号都是分词符(外国人嘛)
这里推荐一篇文章:利用mysql的全文索引实现模糊查询
3.2. MySQL中与全文索引相关的几个变量:
使用命令:mysql> SHOW VARIABLES LIKE 'ft%'; #ft就是FullText的简写
ft_boolean_syntax + -><()~*:""&| #改变IN BOOLEAN MODE的查询字符,不用重新启动MySQL也不用重建索引
ft_min_word_len 4 #最短的索引字符串,默认值为4,(通常改为1)修改后必须重建索引文件
重新建立索引命令:repair table tablename quick
ft_max_word_len 84 #最长的索引字符串,默认值为84,修改后必须重建索引文件
ft_query_expansion_limit 20 #查询括展时取最相关的几个值用作二次查询
ft_stopword_file (built-in) #全文索引的过滤词文件,具体可以参考:MySQL全文检索中不进行全文索引默认过滤词
特别注意:50%的门坎限制(当查询结果很多,几乎所有记录都有,或者极少的数据,都有可能会返回非所期望的结果)
-->可用IN BOOLEAN MODE即可以避开50%的限制。
此时使用全文索引的格式就变成了: SELECT * FROM `student` WHERE MATCH(`name`) AGAINST('聪' IN BOOLEAN MODE)
更多内容请参考:MySQL中的全文检索(1)
ft_boolean_syntax (+ -><()~*:“”&|)使用的例子:
4.1 + : 用在词的前面,表示一定要包含该词,并且必须在开始位置。
eg: +Apple 匹配:Apple123, “tommy, Apple”
4.2 - : 不包含该词,所以不能只用「-yoursql」这样是查不到任何row的,必须搭配其他语法使用。
eg: MATCH (girl_name) AGAINST (‘-林志玲 +张筱雨’)
匹配到: 所有不包含林志玲,但包含张筱雨的记录
4.3. 空(也就是默认情况),表示可选的,包含该词的顺序较高。
例子:
apple banana 找至少包含上面词中的一个的记录行
+apple +juice 两个词均在被包含
+apple macintosh 包含词 “apple”,但是如果同时包含 “macintosh”,它的排列将更高一些
+apple -macintosh 包含 “apple” 但不包含 “macintosh”
4.4. > :提高该字的相关性,查询的结果会排在比较靠前的位置。
4.5.< :降低相关性,查询的结果会排在比较靠后的位置。
例子:4.5.1.先不使用 ><
select * from tommy.girl where match(girl_name) against(‘张欣婷’ in boolean mode);
图片: https://uploader.shimo.im/f/VuSdagMCII8ol86C.png!thumbnail?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJhY2Nlc3NfcmVzb3VyY2UiLCJleHAiOjE2NTE5MTM2MzcsImZpbGVHVUlEIjoiNmpkRGdrdjM2WHBqOEszWSIsImlhdCI6MTY1MTkxMzMzNywidXNlcklkIjozMDMyNTI3OH0.9BytYr6H_6Y6DTjPYwomBGYFEnC6PFmC0-SVQyyXVzQ 可以看到完全匹配的排的比较靠前
4.5.2. 单独使用 >
select * from tommy.girl where match(girl_name) against(‘张欣婷 >李秀琴’ in boolean mode);
图片: https://uploader.shimo.im/f/NGX8aHRzzFKNgOra.png!thumbnail?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJhY2Nlc3NfcmVzb3VyY2UiLCJleHAiOjE2NTE5MTM2MzcsImZpbGVHVUlEIjoiNmpkRGdrdjM2WHBqOEszWSIsImlhdCI6MTY1MTkxMzMzNywidXNlcklkIjozMDMyNTI3OH0.9BytYr6H_6Y6DTjPYwomBGYFEnC6PFmC0-SVQyyXVzQ 使用了>的李秀琴马上就排到最前面了
4.5.3. 单独使用 <
select * from tommy.girl where match(girl_name) against('张欣婷 <不是人' in boolean mode);
图片: https://uploader.shimo.im/f/VISsJZOVusu9HO3p.png!thumbnail?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJhY2Nlc3NfcmVzb3VyY2UiLCJleHAiOjE2NTE5MTM2MzcsImZpbGVHVUlEIjoiNmpkRGdrdjM2WHBqOEszWSIsImlhdCI6MTY1MTkxMzMzNywidXNlcklkIjozMDMyNTI3OH0.9BytYr6H_6Y6DTjPYwomBGYFEnC6PFmC0-SVQyyXVzQ 看到没,不是人也排到最前面了,这里使用的可是 < 哦,说好的降低相关性呢,往下看吧。
4.5.4.同时使用><
select * from tommy.girl where match(girl_name) against('张欣婷 >李秀琴 <练习册 <不是人>是个鬼' in boolean mode);
图片: https://uploader.shimo.im/f/2oMQpk2zRMo0iBrT.png!thumbnail?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJhY2Nlc3NfcmVzb3VyY2UiLCJleHAiOjE2NTE5MTM2MzcsImZpbGVHVUlEIjoiNmpkRGdrdjM2WHBqOEszWSIsImlhdCI6MTY1MTkxMzMzNywidXNlcklkIjozMDMyNTI3OH0.9BytYr6H_6Y6DTjPYwomBGYFEnC6PFmC0-SVQyyXVzQ 到这里终于有答案了,只要使用了 ><的都会往前排,而且>的总是排在<的前面
小结一下:1. 只要使用 ><的总比没用的 靠前;
2. 使用 >的一定比 <的排的靠前 (这就符合相关性提高和降低);
3. 使用同一类的,使用的越早,排的越前。
4.6. ( ):可以通过括号来使用字条件。
eg: +aaa +(>bbb aaa&bbb&ccc > aaa&ccc
4.7. ~ :将其相关性由正转负,表示拥有该字会降低相关性,但不像「-」将之排除,只是排在较后面。
eg: +apple ~macintosh 先匹配apple,但如果同时包含macintosh,就排名会靠后。
4.8. * :通配符,这个只能接在字符串后面。
MATCH (girl_name) AGAINST ('+*ABC*') #错误,不能放前面
MATCH (girl_name) AGAINST ('+张筱雨*') #正确
4.9. " " :整体匹配,用双引号将一段句子包起来表示要完全相符,不可拆字。
eg: "tommy huang" 可以匹配 tommy huang xxxxx 但是不能匹配 tommy is huang。
5.补充:Windows下无法修改 ft_min_word_len的情况,
5. 1. 使用cmd打开 services.msc,
找到你的 MySQL服务,右键Properties,找到你的my.ini所在的路径
图片: https://uploader.shimo.im/f/mCtHsdPFQdBeQ3Jk.png!thumbnail?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJhY2Nlc3NfcmVzb3VyY2UiLCJleHAiOjE2NTE5MTM2MzcsImZpbGVHVUlEIjoiNmpkRGdrdjM2WHBqOEszWSIsImlhdCI6MTY1MTkxMzMzNywidXNlcklkIjozMDMyNTI3OH0.9BytYr6H_6Y6DTjPYwomBGYFEnC6PFmC0-SVQyyXVzQ
5.2. 停止MySQL,在my.ini中增加 ft_min_word_len = 1,重启MySQL,
然后使用命令 show variables like ‘ft_min_word_len’; 查看是否生效了
InnoDB默认的全文索引parser非常合适于Latin,因为Latin是通过空格来分词的。但对于像中文,日文和韩文来说,没有这样的分隔符。一个词可以由多个字来组成,所以我们需要用不同的方式来处理。在MySQL 5.7.6中我们能使用一个新的全文索引插件来处理它们:n-gram parser.
什么是N-gram?
在全文索引中,n-gram就是一段文字里面连续的n个字的序列。例如,用n-gram来对”信息系统”来进行分词,得到的结果如下:
N-gram 例子
如何在InnoDB中使用N-gram Parser?
N-gram parser是默认加载到MySQL中并可以直接使用的。我们只需要在DDL中创建全文索引时使用WITH PARSER ngram。比如,下面的SQL语句在MySQL 5.7.6及更高版本上可以运行。
N-gram DDL示例
我们引入了一个新的全局变量叫ngram_token_size。由它来决定n-gram中n的大小,也就是词的大小。它的默认值是2,这个时候,我们使用的是bigram。它的合法的取值范围是1到10。现在,我们很自然会想到一个问题:实际应用中应该如何设置ngram_token_size值的大小呢?当然,我们推荐使用2。但是你也可以通过如下这个简单的规则来可以选择任何合法的值:设置到你希望能查询到的最小的词的大小。如果你想查询到单个字,那么我们需要设置为1。 ngram_token_size的值设置的越小,全文索引占用的空间也越小。一般来说,查询正好等于ngram_token_size的词,速度会更快,但是查询比它更长的词或短语,则会变慢。
N-gram分词处理
N-gram parser和系统默认的全文索引parser有如下不同点:
词大小检查:因为有了ngram_token_size,所以innodb_ft_min_token_size和innodb_ft_max_token_size将不适用于n-gram。
无用词(stopword)处理:通常,对于一个新的词,我们会查找stopwords表,看是否有匹配的词。如果有,这个词就不会加入到全文索引中。但是在n-gram中,我们会查找stopwords表,看是否包含里面的词。这样处理的原因是,在中日韩的文本中,有很多没有意义的字符,词语和标点符号。比如,如果我们把‘的’加入到stopwords表中,那么对于句子‘信息的系统’,在默认情况下我们分词结果为‘信息’,‘系统’。其中‘息的’和‘的系’被过滤掉了。
我们可以通过查询INFORMATION_SCHEMA.INNODB_FT_INDEX_CACHE和INFORMATION_SCHEMA.INNODB_FT_TABLE_TABLE来查询哪些词在全文索引里面。这是一个非常有用的调试工具。如果我们发现一个包含某个词的文档,没有如我们所期望的那样出现在查询结果中,那么这个词可能是因为某些原因不在全文索引里面。比如,它含有stopword,或者它的大小小于ngram_token_size等等。这个时候我们就可以通过查询这两个表来确认。下面是一个简单的例子:
简单的调试示例
N-gram查询处理
文本查询(Text Searches)
在自然语言模式(NATURAL LANGUAGE MODE)下,文本的查询被转换为n-gram分词查询的并集。例如,(‘信息系统’)转换为(‘信息 息系 系统’)。下面一个例子:
自然语言模式示例
在布尔模式(BOOLEAN MODE),文本查询被转化为n-gram分词的短语查询。例如,(‘信息系统’)转换为(“‘信息 息系 系统’”)。
布尔模式示例
通配符查询(Wildcard Searches)
如果前缀的长度比ngram_token_size小,那么查询结果将返回在全文索引中所有以这个词作为前缀的n-gram的词。
通配符查询示例-1
如果前缀的长度大于等于ngam_token_size,那么这个查询则转换为一个短语(phrase search),通配符则被忽略。例如,(‘信息*’)转换为(‘”信息”‘),(‘信息系*’)转换为(‘”信息 息系”‘)。
通配符查询示例-2
短语查询(Phrase Searches)
短语查询则被转换为n-gram分词的短语查询。比如,(‘信息系统’)转换为(‘”信息 息系 系统”‘)。
短语查询示例
如果您想了解更多关于InnoDB全文索引的详细内容,可以参考用户手册中InnoDB全文索引的部分,还有Jimmy在Dr. Dobb上的精彩文章。如果您想了解更多关于n-gram的详细内容,则可以参考用户手册中n-gram parser的部分。
我们很高兴在MySQL 5.7全文索引中增强对中日韩文的支持,这也是我们工作中很重要的部分,希望这个功能对大家有帮助。如果您有任何问题,可以在本blog中进行评论,提交一个服务需求,或者提交一个bug报告。
1、ngram and MeCab full-text parser plugins
全文检索在MySQL里面很早就支持了,只不过一直以来只支持英文。缘由是他从来都使用空格来作为分词的分隔符,而对于中文来讲,显然用空格就不合适,需要针对中文语义进行分词。但从MySQL 5.7开始,MySQL内置了ngram全文检索插件,用来支持中文分词,并且对MyISAM和InnoDB引擎有效。
2、必要的参数设置
在使用中文检索分词插件ngram之前,先得在MySQL配置文件里面设置他的分词大小(默认是2),比如,
[mysqld]
ngram_token_size=2
分词的SIZE越小,索引的体积就越大,所以要根据自身情况来设置合适的大小。
3、添加全文索引
alter table testtable add fulltext index testfulltext(clumn1,clumn2) with parser ngram;
当然也可以在建表时
CREATE TABLE articles (
id INTUNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
title VARCHAR(200),
body TEXT,
FULLTEXT (title,body) WITH PARSER ngram
) ENGINE=InnoDB CHARACTER SET utf8mb4;
4、查询索引
按自然语言搜索模式查询
SELECT * FROM articles WHERE MATCH (title,body) AGAINST (‘关键词’ IN NATURAL LANGUAGE MODE);
按布尔全文搜索模式查询
2.1 匹配既有管理又有数据库的记录
SELECT * FROM articles WHERE MATCH (title,body) AGAINST (‘+数据库 +管理’ IN BOOLEAN MODE);
2.2匹配有数据库,但是没有管理的记录
SELECT * FROM articles WHERE MATCH (title,body) AGAINST (‘+数据库 -管理’ IN BOOLEAN MODE);
2.3匹配MySQL,但是把数据库的相关性降低
SELECT * FROM articles WHERE MATCH (title,body) AGAINST (‘>数据库 +MySQL’ INBOOLEAN MODE);
最近某个项目出现了查询非常慢的问题, 就是企业名称的模糊查询, 由于不知道关键词的位置, 所以都是 like ‘%keyword%’ 进行全文检索, 正常搜索都要20秒以上. 普通的索引无法生效, 已有项目,不进行框架改造,考虑从数据库优化入手.
整体步骤大概是:
1 Mysql5.7.6版本之后是支持INNODB的中文全文索引的. 创建索引时需要用到 with parser ngram
创建语句:
ALTER TABLE tablename ADD FULLTEXT INDEX tablename_columnname (columnname) with parser ngram;
500w的记录数, 需要大概200秒. 供参考. 索引的创建需要放置到停用词配置完毕之后.
通过
show VARIABLES like ‘%innodb_ft_%’;
可以查看配置
图片: https://uploader.shimo.im/f/WQ40YtMSEntWfqYD.png!thumbnail?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJhY2Nlc3NfcmVzb3VyY2UiLCJleHAiOjE2NTE5MTM2MzcsImZpbGVHVUlEIjoiNmpkRGdrdjM2WHBqOEszWSIsImlhdCI6MTY1MTkxMzMzNywidXNlcklkIjozMDMyNTI3OH0.9BytYr6H_6Y6DTjPYwomBGYFEnC6PFmC0-SVQyyXVzQ
这里我调整了innodb_ft_result_cache_limit的大小,从原来的 2000000000 调大了 4000000000.
由于中文全文索引的关键词是插件分割的, 没细研究,不知道其按语义分割的情况.
自己写了个测试的 innodb_ft_user_stopword_table
结构要跟mysql默认的结构一致:
create table my_innodb_ft_stopword (value varchar(32)) ENGINE = innodb default charset utf8 collate utf8_general_ci;
自己往里面插入了一些stopword避免无效的关键词查询, 停用词的添加可考虑无意义或者查询结果集 >50% 的词语, 比如"有限公司",“公司” 按照n=3的分词策略, 有限股分公司 可拆分为
图片: https://uploader.shimo.im/f/V4fTEmKI95XgSJ62.png!thumbnail?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJhY2Nlc3NfcmVzb3VyY2UiLCJleHAiOjE2NTE5MTM2MzcsImZpbGVHVUlEIjoiNmpkRGdrdjM2WHBqOEszWSIsImlhdCI6MTY1MTkxMzMzNywidXNlcklkIjozMDMyNTI3OH0.9BytYr6H_6Y6DTjPYwomBGYFEnC6PFmC0-SVQyyXVzQ
设置全局的停用词表
SET GLOBAL innodb_ft_user_stopword_table = ‘mysql/my_innodb_ft_stopword’
全文索引主要包括 自然语言模式 IN NATURAL LANGUAGE MODE 和布尔模式 IN BOOLEAN MODE
其中boolean模式中,
“+”表示必须包含
“-”表示必须排除
“>”表示出现该单词时增加相关性
“<”表示出现该单词时降低相关性
“*”表示通配符
“~”允许出现该单词,但是出现时相关性为负
““””表示短语
官方解析:
‘“some words”’
Find rows that contain the exact phrase “some words” (for example, rows that contain “some words of wisdom” but not “some noise words”). Note that the " characters that enclose the phrase are operator characters that delimit the phrase. They are not the quotation marks that enclose the search string itself.
""双引号效果 类同于 like ‘%keyword%’
执行全文索引创建脚本.
查询效率对比:
图片: https://uploader.shimo.im/f/RCrNvspPakgZmaka.png!thumbnail?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJhY2Nlc3NfcmVzb3VyY2UiLCJleHAiOjE2NTE5MTM2MzcsImZpbGVHVUlEIjoiNmpkRGdrdjM2WHBqOEszWSIsImlhdCI6MTY1MTkxMzMzNywidXNlcklkIjozMDMyNTI3OH0.9BytYr6H_6Y6DTjPYwomBGYFEnC6PFmC0-SVQyyXVzQ
官方资料链接: https://dev.mysql.com/doc/refman/5.7/en/innodb-fulltext-index.html
1.概要
InnoDB引擎对FULLTEXT索引的支持是MySQL5.6新引入的特性,之前只有MyISAM引擎支持FULLTEXT索引。对于FULLTEXT索引的内容可以使用MATCH()…AGAINST语法进行查询。
为了在InnoDB驱动的表中使用FULLTEXT索引MySQL5.6引入了一些新的配置选项和INFORMATION_SCHEMA表。比如,为了监视一个FULLTEXT索引中文本处理过程的某一方面可以查询INNODB_FT_CONFIG,INNODB_FT_INDEX_TABLE,INNODB_FT_INDEX_CACHE,INNODB_FT_DEFAULT_STOPWORD,INNODB_FT_DELETED和INNODB_FT_BEING_DELETED这些表。可以通过innodb_ft_num_word_optimize和innodb_optimize_fulltext_only选项控制OPTIMIZETABLE命令对InnoDB FULLTEXT索引的更新。
2.相关库表
INFORMATION_SCHEMA库中与InnoDB全文索引相关的表如下:
Ø INNODB_SYS_INDEXES:提供了InnoDB索引的状态信息。
Ø INNODB_SYS_TABLES:提供了InnoDB表的状态信息。
Ø INNODB_FT_CONFIG:显示一个InnoDB表的FULLTEXT索引及其相关处理的元数据。
Ø INNODB_FT_INDEX_TABLE:转化后的索引信息用于处理基于InnoDB表FULLTEXT索引的文本搜索。一般用于调试诊断目的。使用该表前需先配置innodb_ft_aux_table配置选项,将其指定为想要查看的含FULLTEXT索引的InnoDB表,选项值的格式为database_name/table_name。配置了该选项后INNODB_FT_INDEX_TABLE,INNODB_FT_INDEX_CACHE, INNODB_FT_CONFIG, INNODB_FT_DELETED和INNODB_FT_BEING_DELETED表将被填充与innodb_ft_aux_table配置选项指定的表关联的搜索索引相关信息。
Ø INNODB_FT_INDEX_CACHE:向含FULLTEXT索引的InnoDB表插入数据后新插入数据转后的索引信息。表结构与INNODB_FT_INDEX_TABLE一致。为含FULLTEXT索引的InnoDB表执行DML操作期间重组索引开销很大,因此将新插入的被索引的词单独存储于该表中,当且仅当为InnoDB表执行OPTIMIZE TABLE语句后才将新的转换后的索引信息与原有的主索引信息合并。使用该表前需先配置innodb_ft_aux_table配置选项。
Ø INNODB_FT_DEFAULT_STOPWORD:在InnoDB表上创建FULLTEXT索引所使用的默认停止字表。
Ø INNODB_FT_DELETED:记录了从InnoDB表FULLTEXT索引中删除的行。为了避免为InnoDB的FULLTEXT索引执行DML操作期间重组索引的高开销,新删除的词的信息单独存储于此表。当且仅当为此InnoDB表执行了OPTIMIZE TABLE操作后才会从主搜索索引中移除已删除的词信息。使用该表前需先配置innodb_ft_aux_table选项。
Ø INNODB_FT_BEING_DELETED:为含FULLTEXT索引的InnoDB表执行OPTIMIZE TABLE操作时会根据INNODB_FT_DELETED表中记录的文档ID从InnoDB表的FULLTEXT索引中删除相应的索引信息。而INNOFB_FT_BEING_DELETED表用于记录正在被删除的信息,用于监控和调试目的。
3.相关配置选项
Ø innodb_ft_aux_table:指定包含FULLTEXT索引的InnoDB表的的名称。该变量在运行时设置用于诊断目的。设置该值后INNODB_FT_INDEX_TABLE, INNODB_FT_INDEX_CACHE, INNODB_FT_CONFIG,INNODB_FT_DELETED和INNODB_FT_BEING_DELETED表将被填充与innodb_ft_aux_table指定的表关联的搜索索引相关信息。
Ø innodb_ft_cache_size:当创建一个InnoDB FULLTEXT索引时在内存中存储已解析文档的缓存大小。
Ø innodb_ft_enable_diag_print:是否开启额外的全文搜索诊断输出。
Ø innodb_ft_enable_stopword:是否开启停止字。InnoDB FUllTEXT索引被创建时为其指定一个关联的停止字集。(若设置了innodb_ft_user_stopword_table则停止字由该选项指定的表获取,若没有设置innodb_ft_user_stopword_table而设置了innodb_ft_server_stopword_table则停止字由该选项指定的表获取,否则使用内置的停止字。)
Ø innodb_ft_max_token_size:存储在InnoDB的FULLTEXT索引中的最大词长。设置这样一个限制后可通过忽略过长的关键字等有效降低索引大小从而加速查询。
Ø innodb_ft_min_token_size:存储在InnoDB的FULLTEXT索引中的最小词长。增加该值后会忽略掉一些通用的没有显著意义的词汇从而降低索引大小继而加速查询。
Ø innodb_ft_num_word_optimize:为InnoDB FULLTEXT索引执行OPTIMIZE操作每次所处理的词数。因为在含有全文搜索索引的表中执行批量的插入或更新操作需要大量的索引维护操作来合并所有的变化。因此,一般会运行一系列OPTIMIZE TABLE语句,每次从上一次的位置开始,处理指定数目的词,知道搜索索引被完全更新。
Ø innodb_ft_server_stopword_table:含有停止字的表,在创建InnoDB FULLTEXT索引时或忽略表中的停止字。停止字表需为InnoDB表,且在指定前应当已存在。
Ø innodb_ft_sort_pll_degree:为较大的表构建搜索索引时用于索引和记号化文本的并行线程数。
Ø innodb_ft_user_stopword_table:含有停止字的表,在创建InnoDB FULLTEXT索引时或忽略表中的停止字。停止字表需为InnoDB表,且在指定前应当已存在。
Ø innodb_optimize_fulltext_only:改变OPTIMIZE TABLE语句对InnoDB表操作的方式。对含FULLTEXT 索引的InnoDB表进行维护操作期间,一般临时的开启该选项。默认情况下,OPTIMIZE TABLE语句会重组表的聚集索引中的数据。若开启了该选项则该语句会跳过表数据的重组,而是只处理FULLTEXT索引中新插入的、删除的、更新的标记数据。(在对作为FULLTEXT索引的一部分的InnoDB表列进行了大量的插入、更新或删除操作后,先将innodb_optimize_fulltext_only设置为on以改变OPTIMIZE TABLE的默认行为,然后设置innodb_ft_num_word_optimize为合适的值以将索引维护时间控制在一个合理的可接受范围内,最后执行一系列的OPTIMIZE语句知道搜索索引被完全更新。)
4.全文搜索功能
全文搜索的语法:MATCH(col1,col2,…) AGAINST (expr[search_modifier])。其中MATCH中的内容为已建立FULLTEXT索引并要从中查找数据的列,AGAINST中的expr为要查找的文本内容,search_modifier为可选搜索类型。search_modifier的可能取值有:IN NATURAL LANGUAGEMODE、IN NATURAL LANGUAGE MODE WITH QUERY EXPANSION、IN BOOLEAN MODE、WITH QUERY EXPANSION。search_modifier的每个取值代表一种类型的全文搜索,分别为自然语言全文搜索、带查询扩展的自然语言全文搜索、布尔全文搜索、查询扩展全文搜索(默认使用IN NATURAL LANGUAGE MODE)。
MySQL中全文索引的关键字为FULLTEXT,目前可对MyISAM表和InnoDB表的CHAR、VARCHAR、TEXT类型的列创建全文索引。全文索引同其他索引一样,可在创建表是由CREATE TABLE语句创建也可以在表创建之后用ALTER TABLE或者CREATE INDEX命令创建(对于要导入大量数据的表先导入数据再创建FULLTEXT索引比先创建索引后导入数据会更快)。
4.1自然语言全文搜索
自然语言全文搜索是MySQL全文搜索的默认搜索方式,实现从一个文本集合中搜索给定的字符串。这里,文本集合指的是指由FULLTEXT索引的一个或者多个列。
建表,并给title,body字段加FULLTEXT索引
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 will 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 …’);
例1:
SELECT * FROM articles
WHERE MATCH (title,body)
AGAINST (‘database’ IN NATURAL LANGUAGE MODE);
图片: https://uploader.shimo.im/f/wSNU13jaQswtHjo1.png!thumbnail?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJhY2Nlc3NfcmVzb3VyY2UiLCJleHAiOjE2NTE5MTM2MzcsImZpbGVHVUlEIjoiNmpkRGdrdjM2WHBqOEszWSIsImlhdCI6MTY1MTkxMzMzNywidXNlcklkIjozMDMyNTI3OH0.9BytYr6H_6Y6DTjPYwomBGYFEnC6PFmC0-SVQyyXVzQ
可以看到,语句查找到了包含指定内容的行。实际上,返回的行是按与所查找内容的相关度由高到低的顺序排列的。这个相关度的值由WHERE语句中的MATCH (…) AGAINST (…)计算所得,是一个非负浮点数。该值越大表明相应的行与所查找的内容越相关,0值表明不相关。该值基于行中的单词数、行中不重复的单词数、文本集合中总单词数以及含特定单词的行数计算得出。
例2:
由上例可知MATCH (…) AGAINST (…)实际上会计算一个相关值,可通过下例来验证。
SELECT id, MATCH (title,body)
AGAINST (‘Tutorial’ IN NATURAL LANGUAGE MODE) AS score
FROM articles;
图片: https://uploader.shimo.im/f/F6pjxes8VfEd9Mer.png!thumbnail?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJhY2Nlc3NfcmVzb3VyY2UiLCJleHAiOjE2NTE5MTM2MzcsImZpbGVHVUlEIjoiNmpkRGdrdjM2WHBqOEszWSIsImlhdCI6MTY1MTkxMzMzNywidXNlcklkIjozMDMyNTI3OH0.9BytYr6H_6Y6DTjPYwomBGYFEnC6PFmC0-SVQyyXVzQ
可以看到,所得结果的第二列即为改行与查找内容的相关度。上例1中所得结果的顺序就是按此相关度排列的。
例3:
若想既看到查找到的结果又需要了解具体的相关度,可用下述方法达成。
SELECT id, body, MATCH (title,body) AGAINST
(‘Security implications of running MySQL as root’
IN NATURAL LANGUAGE MODE) AS score
FROM articles WHERE MATCH (title,body) AGAINST
(‘Security implications of running MySQL as root’
IN NATURAL LANGUAGE MODE);
图片: https://uploader.shimo.im/f/d3Xc3fK2K3GNQdDA.png!thumbnail?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJhY2Nlc3NfcmVzb3VyY2UiLCJleHAiOjE2NTE5MTM2MzcsImZpbGVHVUlEIjoiNmpkRGdrdjM2WHBqOEszWSIsImlhdCI6MTY1MTkxMzMzNywidXNlcklkIjozMDMyNTI3OH0.9BytYr6H_6Y6DTjPYwomBGYFEnC6PFmC0-SVQyyXVzQ
可以看到,通过在查找部分和条件部分分别使用相同的MATCH(…) AGAINST(…)可以同时获取两方面的内容(不会增加额外开销,优化器知道两个MATCH(…) AGAINST(…)是相同的,只会执行一次该语句)
注意事项
默认情况下全文搜索大小写不敏感,如上例1,查找的内容为‘database’但含有‘DataBase’的行也会返回。可以通过为FULLTEXT索引列所使用的字符集指定一个特定的校对集来改变这种行为。
考虑下述两个SELECT语句:
图片: https://uploader.shimo.im/f/KYxeNIDpdoX3u6hi.png!thumbnail?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJhY2Nlc3NfcmVzb3VyY2UiLCJleHAiOjE2NTE5MTM2MzcsImZpbGVHVUlEIjoiNmpkRGdrdjM2WHBqOEszWSIsImlhdCI6MTY1MTkxMzMzNywidXNlcklkIjozMDMyNTI3OH0.9BytYr6H_6Y6DTjPYwomBGYFEnC6PFmC0-SVQyyXVzQ
找到包含MySQL或者YourSQL的行
例3:
SELECT * FROM articles
WHERE MATCH (title,body)
AGAINST (‘+MySQL+YourSQL’ IN BOOLEAN MODE);
图片: https://uploader.shimo.im/f/uJCMR9MWPq58cQUz.png!thumbnail?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJhY2Nlc3NfcmVzb3VyY2UiLCJleHAiOjE2NTE5MTM2MzcsImZpbGVHVUlEIjoiNmpkRGdrdjM2WHBqOEszWSIsImlhdCI6MTY1MTkxMzMzNywidXNlcklkIjozMDMyNTI3OH0.9BytYr6H_6Y6DTjPYwomBGYFEnC6PFmC0-SVQyyXVzQ
找到包含同时MySQL和YourSQL的行
例4:
SELECT * FROM articles
WHERE MATCH (title,body)
AGAINST (‘+MySQL YourSQL’ IN BOOLEAN MODE);
图片: https://uploader.shimo.im/f/Fjmt9BrC87SR3GNU.png!thumbnail?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJhY2Nlc3NfcmVzb3VyY2UiLCJleHAiOjE2NTE5MTM2MzcsImZpbGVHVUlEIjoiNmpkRGdrdjM2WHBqOEszWSIsImlhdCI6MTY1MTkxMzMzNywidXNlcklkIjozMDMyNTI3OH0.9BytYr6H_6Y6DTjPYwomBGYFEnC6PFmC0-SVQyyXVzQ
找到必须包含MySQl的行,YourSQL可有可无,但有YourSQL会增加相关性。
例5:
SELECT * FROM articles
WHERE MATCH (title,body)
AGAINST (‘+MySQL ~YourSQL’ INBOOLEAN MODE);
图片: https://uploader.shimo.im/f/ehp3lYX5fHhqSyNC.png!thumbnail?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJhY2Nlc3NfcmVzb3VyY2UiLCJleHAiOjE2NTE5MTM2MzcsImZpbGVHVUlEIjoiNmpkRGdrdjM2WHBqOEszWSIsImlhdCI6MTY1MTkxMzMzNywidXNlcklkIjozMDMyNTI3OH0.9BytYr6H_6Y6DTjPYwomBGYFEnC6PFmC0-SVQyyXVzQ
找到包含必须包含MySQL的行,YourSQL可有可无,若出现了YourSQL则会降低其所在行的相关性。
例6:
SELECT * FROM articles
WHERE MATCH (title,body)
AGAINST (‘+MySQL +(>Security
找到必须同时包含MySQL以及Security或Optimizing的行Security会增加所在行的相关性,而Optimizing会降低所在行的相关性。
例7:
SELECT * FROM articles
WHERE MATCH (title,body)
AGAINST (‘da*’ IN BOOLEAN MODE);
图片: https://uploader.shimo.im/f/8hipFJXHiZ0EazW6.png!thumbnail?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJhY2Nlc3NfcmVzb3VyY2UiLCJleHAiOjE2NTE5MTM2MzcsImZpbGVHVUlEIjoiNmpkRGdrdjM2WHBqOEszWSIsImlhdCI6MTY1MTkxMzMzNywidXNlcklkIjozMDMyNTI3OH0.9BytYr6H_6Y6DTjPYwomBGYFEnC6PFmC0-SVQyyXVzQ
找到包含da*的行。如包含DataBase、database等。
例8:
SELECT * FROM articles
WHERE MATCH (title,body)
AGAINST(‘“MySQL,Tutorial”’ IN BOOLEAN MODE);
图片: https://uploader.shimo.im/f/xKVABUWgpiOnkTBJ.png!thumbnail?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJhY2Nlc3NfcmVzb3VyY2UiLCJleHAiOjE2NTE5MTM2MzcsImZpbGVHVUlEIjoiNmpkRGdrdjM2WHBqOEszWSIsImlhdCI6MTY1MTkxMzMzNywidXNlcklkIjozMDMyNTI3OH0.9BytYr6H_6Y6DTjPYwomBGYFEnC6PFmC0-SVQyyXVzQ
找到包含“MySQL Tutorial”短语的行。
布尔全文搜索的一些特点
Ø MyISAM全文搜索会忽略至少在一半以上数据行中出现的单词(也即所谓的50%阈值),InnoDB无此限制。而在布尔全文搜索中MyISAM的50%阈值不生效。
Ø 停止字列表也适用于布尔全文搜索。
Ø 最小和最大词长全文搜索参数也适用于布尔全文搜索
Ø MyISAM中的布尔搜索在FULLTEXT索引不存在的时候仍可工作,但速度很慢。而InnoDB表的各类全文搜索必须有FULLTEXT索引,否则会出现找不到与指定列相匹配的FULLTEXT索引的错误
Ø InnoDB中的全文搜索不支持在单一搜索单词前使用多个操作符如“++MySQL”。MyISAM中全文搜索可以处理这种情况,但是会忽略除了紧邻单词之外的其他操作符。
4.3查询扩展全文搜索
某些时候我们通过全文搜索来查找包含某方面内容的行,比如我们搜索“database”,实际上我们期望返回结果不仅仅是仅包含“database”单词的行,一些包含“MySQL”、“SQLServer”、“Oracle”、“DB2”、“RDBMS”等的行也期望被返回。这个时候查询扩展全文搜索就能大显身手。
通过在AGAINST()函数中指定WITHQUERY EXPANSION 或者IN NATURAL MODE WITH QUERY EXPANSION可以开启查询扩展全文搜索模式。其工作原理是执行两次搜索,第一次用给定的短语搜索,第二次使用给定的短语结合第一次搜索返回结果中相关性非常高的一些行进行搜索。
例1:
SELECT * FROM articles
WHERE MATCH (title,body)
AGAINST (‘database’ IN NATURAL LANGUAGE MODE);
图片: https://uploader.shimo.im/f/FRia4KUfllfL3SoP.png!thumbnail?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJhY2Nlc3NfcmVzb3VyY2UiLCJleHAiOjE2NTE5MTM2MzcsImZpbGVHVUlEIjoiNmpkRGdrdjM2WHBqOEszWSIsImlhdCI6MTY1MTkxMzMzNywidXNlcklkIjozMDMyNTI3OH0.9BytYr6H_6Y6DTjPYwomBGYFEnC6PFmC0-SVQyyXVzQ
使用自然语言搜索返回了包含“database”的行。
例2:
SELECT * FROM articles
WHERE MATCH (title,body)
AGAINST (‘database’ WITH QUERY EXPANSION);
图片: https://uploader.shimo.im/f/3X1PVSrHPwTBzfTR.png!thumbnail?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJhY2Nlc3NfcmVzb3VyY2UiLCJleHAiOjE2NTE5MTM2MzcsImZpbGVHVUlEIjoiNmpkRGdrdjM2WHBqOEszWSIsImlhdCI6MTY1MTkxMzMzNywidXNlcklkIjozMDMyNTI3OH0.9BytYr6H_6Y6DTjPYwomBGYFEnC6PFmC0-SVQyyXVzQ
使用查询扩展全文搜索,不进返回了包含“database”的行,也返回了与例1中返回的行的内容相关的行。
注意事项
因为查询扩展会返回一些不相关的内容,因此会显著的引入噪声。索引仅当要查询的短语较短时才在考虑使用查询扩展全文搜索。
4.4全文搜索的停止字
上文已经简单介绍过了停止字列表,这里做详细介绍。停止字列表用MySQL Server所使用的字符集和校对集(分别由character_set_server和collation_server两个参数控制)载入并执行搜索。若用于全文索引和搜索的停止字文件或者停止字表使用了与MySQL Server不同的字符集和校对集会则导致查找停止字时错误的命中或未命中。
停止字查找的大小写敏感性也依赖于MySQL Server所使用的校对集,例如校对集为latin1_swedish_ci则查找是大小写不敏感的,若校对集为latin1_geberal_cs或者latin1_bin则查找是大小写敏感的。
InnoDB默认的停止字列表相对较短(因为技术上的或者文学等方面的文档常使用较短的词作为关键字或者有其他显著意义)。InnoDB默认的停止字列表存储在information_schema.innodb_ft_default_stopword表中。当然也可以通过自定义与innodb_ft_default_stopword表结构相同的表,填充期望的停止字,然后通过innodb_ft_server_stopword_table选项指定自定义的停止字表db_name/table_name,来改变默认的行为。另外还可以为innodb_ft_user_stopword_table选项指定含停止字的表,若同时指定了innodb_ft_default_stopword和innodb_ft_user_stopword_table则将使用后者指定的停止字表。上述操作改变所使用停止字表的操作需在创建全文索引前完成。且在指定所使用的停止字表时,表必须已经存在。
对于MyISAM可通过 ft_stopword_file选项指定所使用的停止字列表。MyISAM默认的停止字列表可在MySQL源码的 storage/myisam/ft_static.c文件中找到。
4.5全文搜索的限制
Ø 目前只有InnoDB和MyISAM引擎支持全文搜索。其中InnodB表对FULLTEXT索引的支持从MySQL5.6.4开始。
Ø 分区表不支持全文搜索。
Ø 全文索引适用于多数多字节字符集。例外情况是:对于Unicode,utf8字符集可用但ucs2字符集不适用。尽管不能在ucs2列建立FULLTEXT索引,但可以在MyISAM表IN BOOLEAN MODE模式的搜索中搜索没有建立FULLTEXT索引的列。utf8的特性适用于utf8mb4,ucs2的特性适用于utf16、utf16e和utf32。
Ø 表意型语言如汉语、日语没有诸如空格之类的单词定界符。因此FULLTEXT解析器不能确定此类语言中词的起止。对于此种情况要特殊处理(比如将中文转换成一种单字节类似英文习惯的存储方式)。
Ø 允许在同一表中使用多种字符集,但FULLTEXT索引中的列必须使用同一字符集和校对集。
Ø MATCH()函数中的列必须与FULLTEXT索引中定义的列完全一致,除非是在MyISAM表中使用IN BOOLEAN MODE模式的全文搜索(可在没有建立索引的列执行搜索,但速度很慢)。
Ø AGAINST()函数中的参数需为在查询评估期间保持不变的字符串常量。
Ø FULLTEXT搜索的索引提示比non-FULLTEXT搜索的索引提示要多一些限定:对于自然语言模式的全文搜索,索引提示会被忽略而不给出任何提示,比如虽明确在查询语句中给出了IGNORE INDEX(i)指明不使用i索引,但是该索引提示会被忽略掉,最终的查询中仍会使用索引i;对于布尔模式的全文搜索,FOR ORDER BY和FOR GROUP BY的索引提示会被忽略,FOR JOIN和不带FOR修饰符的索引提示不被忽略。
4.6全文搜索参数调整
仅有少量的用户可调参数用于调整MySQL的全文搜索能力。可以通过修改源码来获取更多对MySQL全文搜索行为的控制。但一般情况下不推荐这么做,除非很清楚自己在做什么,因为这些参数已经针对效率做过调整,修改默认的行为多数情况下反而会带来性能下降。
多数全文搜索相关的变量不能在Server运行的时候修改。需在Server启动时指定这些参数,或者修改完参数之后重新启动Server。另外,某些变量修改后需要重建FULLTEXT索引。
控制最小、最大字长的配置选项对于InnoDB为:innodb_ft_min_token_size和innodb_ft_max_token_size,对于MyISAM为:ft_min_word_len 和 ft_max_word_len。改变这些选项中任意一个的值都需重建FULLTEXT索引并重启Server。
用于停止字列表的配置选项对于InnoDB为:innodb_ft_enable_stopword、innodb_ft_server_stopword_table和innodb_ft_user_stopword_table,对于MyISAM为:ft_stopword_file。可以通过改变这些选项的值来开启/关闭停止字过滤并指定停止字列表。修改了这些选项后需重建索引并在必要的时候重启Server。
ft_stopword_file指定了包含停止字列表的文件,Server默认在数据目录搜索该文件除非用绝对路径指定了文件位置,若文件内容为空,则会关闭MyISAM的停止字过滤功能。停止字文件格式很灵活,可以使用任何非字母或数字的字符来界定停止字,但“”和“’”例外,它们会被当作字的一部分处理。停止字列表使用Server默认的字符集。
MyISAM全文搜索的50%阈值特性可通过修改源码来关闭,将源码storage/myisam/ftdefs.h中的宏#define GWS_IN_USEGWS_PROB替换为#define GWS_IN_USE GWS_FREQ后重新编译MySQL即可。同样,不推荐上述方式,如果确实需要搜索一些通用的词,可以用布尔模式的全文搜获,此种情况下50%阈值特性不生效。
可以通过修改ft_boolean_syntax选项的值来更改MyISAM布尔全文搜做中默认使用的操作符(InnoDB无此选项)。该选项可动态改变但须超级用户权限,另外,改变了改制后无需重建FULLTEXT索引。
可以通过多种方式更改期望被认作是单词字符成分的字符集合。默认情况下“”和“’”以及字母和数字被认为是组成单词的字符,其他的被默认为定界符。例如,我们现在想把连字符“-”也作为组成单词的字符处理,那么可以通过如下方式完成:
Ø 修改MySQL源码,在storage/myisam/ftdefs.h文件中找到true_word_char()和misc_word_char()两个宏,在任一个宏定义里添加“-”,重新编译MySQL。
Ø 修改字符集文件,true_word_char()宏实际上利用“character type”表来从其他字符中区分出字母和数字。可以通过编辑字符集对应的XML文件中节点中的内容来将“-”指定为“字母“,然后将该字符集用于FULLTEXT索引。此种方式无需重新编译MySQL。对于编辑字符集XML文件,可参阅MySQL参考手册CharacterDefinition Arrays部分。
http://dev.mysql.com/doc/refman/5.6/en/character-arrays.html
Ø 对FULLTEXT索引列使用的字符集添加新的校对集,然后更新该列以使用新添加的校对集。具体参阅MySQL手册Adding a Collation to a Character Set以及Adding a Collation for Full-Text Indexing部分。
http://dev.mysql.com/doc/refman/5.6/en/full-text-adding-collation.html
http://dev.mysql.com/doc/refman/5.6/en/adding-collation.html
为InnoDB表重建FULLTEXT索引可以通过带DROP INDEX和ADD INDEX从句的ALTER TABLE语句完成,先删除旧的再创建新的。为MyISAM表重建FULLTEXT索引同样可通过上述语句完成,也可以通过QUICK repair操作来重建(但通常第一种方式会更快),如:
mysql> REPAIR TABLE tbl_name QUICK;
需要特别说明的是,若通过repair表的方式来为MyISAM表重建FULLTEXT索引,则通过上述语句进行即可。用myisamchk工具也可以为MyISAM表重建索引,但是容易导致查询产生错误的结果,对表的修改可能使Server认为该表被损坏了。究其原因是因为通过myisamchk工具执行修改MyISAM表的索引的操作时,除非明确指定了要使用的参数值否则使用默认的全文索引参数值(如最小最大词长等)重建FULLTEXT索引。导致这种情况是因为只有Server才知道这些全文索引参数值,MyISAM索引文件中不存储这些值。若更改过了这些值,如设置了ft_min_word_len=2,则在通过myisamchk工具修复表时要明确指定该修改过的参数值如:
shell> myisamchk --recover–ft_min_word_len=3 tbl_name.MYI
当然也可以通过在MySQL配置文件[myisamchk]节中加入同[mysqld]节中与全文搜索相关参数一致的参数来确保myisamchk使用最新的参数值来重建表的FULLTEXT索引。
用myisamchk为MyISAM表修改索引的替代方式是使用REPAIR TABLE、ANALYZE TABLE、 OPTIMIZE TABLE、ALTER TABLE,这些语句是由Server执行的因此可以读取到正确的全文索引参数值,不会引起问题。
4.7为全文搜索添加校对字符集
参考
10.4. Adding a Collation to a Character Set
http://dev.mysql.com/doc/refman/5.6/en/adding-collation.html
12.9.7. Adding a Collation for Full-Text Indexing
http://dev.mysql.com/doc/refman/5.6/en/full-text-adding-collation.html
5.性能对比测试
5.1测试环境
测试机:SVR644HP380
内存容量:8G
MySQL Server版本:5.6.12
5.2测试设计
词汇量:6个等级,分别用vocab01k、vocab05k、vocab10k、vocab15k,vocab25k、vocab35k标记,每个等级的词汇数如下,1000、5000、10000、15000、25000、35000。(取牛津词典单词部分,去重复后随机打乱顺序,分别截取前1000、5000、10000……作为对应的词汇量)
记录数:20个等级,分别用rec005k、rec010k、rec015k、rec020k、……rec095k、rec100k标记,每个等级的记录数如下,5000、10000、15000、20000、25000、30000、……、95000、100000。
根据词汇量等级和记录数等级分别生成含不同记录数且表中文本列是由对应的词汇量生成的随机文本的表,共620=120个。表的存储引擎使用InnoDB。表由id和body两个字段组成,分别为整型和文本型,且在body列创建了FULLTEXT索引。表名的命名规则为vocab01k_rec005k,表示该表中共含有5千条记录,每条记录中的body列由vocab01k对应的词汇量生成的随机单词组成,以此类推。每行记录中的body列定为由50个随机单词组成。
比较两类查询:LIKE从句查询以及使用FULLTEXT索引的MATCH()AGAINST()查询。在每个表上分别执行LIKE查询和MATCH() AGAINST()全文查询,每个表上的每个查询分别执行50次,记录每次所耗费的时间。对于每50个消耗的时间,删除其最大两个值和最小两个值,取剩余值的均值作为查询耗时的最终结果。这样一共可获得1202 = 240个时间数据,根据这些数据绘图。在每个表上执行的查询如下(其中random_word1、random_word2、random_word3是根据查询时表对应的词汇量生成的随机单词。):
LIKE搜索:
SELECT body FROM table_name WHERE body LIKE “%random_word1%” AND bodyLIKE “% random_word2%” AND body LIKE “% random_word3%”;
FULLTEXT搜索:
SELECT body FROM table_name WHERE MATCH(body) AGAINST(“+random_word3 + random_word3+ random_word3” IN BOOLEAN MODE)
5.3测试结果
图示
LIKE搜索:
图片: https://uploader.shimo.im/f/z8QWbPbDdbXStnsC.png!thumbnail?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJhY2Nlc3NfcmVzb3VyY2UiLCJleHAiOjE2NTE5MTM2MzcsImZpbGVHVUlEIjoiNmpkRGdrdjM2WHBqOEszWSIsImlhdCI6MTY1MTkxMzMzNywidXNlcklkIjozMDMyNTI3OH0.9BytYr6H_6Y6DTjPYwomBGYFEnC6PFmC0-SVQyyXVzQ
FULLTEXT搜索:
图片: https://uploader.shimo.im/f/gGu0ugqsmCG4tYOV.png!thumbnail?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJhY2Nlc3NfcmVzb3VyY2UiLCJleHAiOjE2NTE5MTM2MzcsImZpbGVHVUlEIjoiNmpkRGdrdjM2WHBqOEszWSIsImlhdCI6MTY1MTkxMzMzNywidXNlcklkIjozMDMyNTI3OH0.9BytYr6H_6Y6DTjPYwomBGYFEnC6PFmC0-SVQyyXVzQ
FULLTEXT搜索与LIKE搜索对比:
图片: https://uploader.shimo.im/f/hgLA113VI5JrAP29.png!thumbnail?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJhY2Nlc3NfcmVzb3VyY2UiLCJleHAiOjE2NTE5MTM2MzcsImZpbGVHVUlEIjoiNmpkRGdrdjM2WHBqOEszWSIsImlhdCI6MTY1MTkxMzMzNywidXNlcklkIjozMDMyNTI3OH0.9BytYr6H_6Y6DTjPYwomBGYFEnC6PFmC0-SVQyyXVzQ
结果讨论
LIKE搜索的耗时随着记录数的增加而线性增长,但对于10万行记录以下的表(这里共10000050个单词)搜索时间基本上能保持在1秒以内,所以like搜索的性能也不是特别差。由不同词汇量生成的文本对LIKE搜索的性能影响不大,不同词汇量对应的搜索时间基本上在一个很小的时间范围内变化。
FULLTEXT搜索耗时也随表中记录数的增长而线性增加。对于10万行记录以下的表(这里共10000050个单词)搜索时间基本上能保持在0.01秒以内。由不同词汇量生成的随机文本对FULLTEXT搜索性能有相对来说比较显著的影响。每行记录中含同样的单词数,这样,较大的词汇量倾向于生成冗余度更低的文本,相应的搜索耗时倾向于更少。这可能与FULLTEXT索引建立单词索引的机制有关,较大的词汇量倾向于生成范围广但相对较浅的索引,因而能快速确定文本是否匹配。
与LIKE搜索相比,FULLTEXT全文搜索的性能要强很多,对于10万行记录的表,搜索时间都在0.02秒以下。因此可以将基于FULLTEXT索引的文本搜索部署于网站项目中的文本搜索功能中。但是,正如上述提到的,无论是LIKE搜索还是FULLTEXT搜索,其性能都会随着记录数的增长而下降,因此,若网站项目中的文本搜索数据库记录数庞大的一定规模后,可能需要考虑使用MySQL数据库全文搜索以外的文本搜索解决方案了。
以上内容转自:
https://blog.csdn.net/u011734144/article/details/52817766
https://www.cnblogs.com/zhoujinyi/p/5643408.html
https://blog.csdn.net/YSOLA4/article/details/96830203
https://blog.csdn.net/qq_33663251/article/details/69612619
https://blog.csdn.net/zyz511919766/article/details/12780173