快速全文搜索在 MySQL 中按 id 排序的精确短语的一次出现

Posted

技术标签:

【中文标题】快速全文搜索在 MySQL 中按 id 排序的精确短语的一次出现【英文标题】:Fast FullText search for one occurrence of exact phrase in MySQL sorted by id 【发布时间】:2017-11-27 13:33:45 【问题描述】:

我正在使用 mysql/InnoDb。我有这张表News,它有大约 3,000,000 条记录,我希望它还有 7,000,000 条记录。目前,我使用以下查询从News 表中获取记录,并在其titlecontent 中使用确切的短语:

SELECT * FROM News WHERE MATCH (title, content) AGAINST ('"My Phrase"' in Boolean Mode)

但结果并不令人满意。对我来说,在搜索一个确切的短语时,首先查看该短语的最新消息是合乎逻辑的。我不需要按相关性对结果进行排序。我不介意找到的第一条记录是否有 1 次出现我的短语,而第二条记录有 10 次。我只关心 ORDER BY time_addedORDER BY id。所以我尝试了:

SELECT * FROM News WHERE MATCH (title, content) AGAINST ('"My Phrase"' in Boolean Mode) ORDER BY id DESC limit 40

但是当我尝试EXPLAIN这个查询时,我看到它是Using filesort,正如我所期望的那样。

这是 MySQL 工作的唯一方式吗?我能否以最佳方式获得按时间排序的结果?

编辑:

MySQL 版本:5.6.34

News表结构如下:

CREATE TABLE `News` (
 `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
 `title` varchar(400) COLLATE utf8_bin NOT NULL,
 `url_title` varchar(80) COLLATE utf8_bin NOT NULL,
 `link` varchar(2000) COLLATE utf8_bin NOT NULL,
 `link_unique_index` varchar(255) COLLATE utf8_bin DEFAULT NULL,
 `source` smallint(5) unsigned NOT NULL COMMENT,
 `category` smallint(5) unsigned NOT NULL DEFAULT '0',
 `description` varchar(600) COLLATE utf8_bin NOT NULL,
 `has_content` tinyint(1) NOT NULL DEFAULT '0',
 `content` text COLLATE utf8_bin NOT NULL,
 `has_image` tinyint(1) NOT NULL,
 `image` varchar(14) COLLATE utf8_bin NOT NULL,
 `image_orientation` char(1) COLLATE utf8_bin NOT NULL,
 `original_image` varchar(2000) COLLATE utf8_bin NOT NULL,
 `keywords` varchar(300) COLLATE utf8_bin NOT NULL,
 `year_added` smallint(4) NOT NULL,
 `date_added` date NOT NULL,
 `time_added` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
 `time_updated` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
 `number_of_views` int(11) NOT NULL DEFAULT '0',
 `last_view_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
 `number_of_positives` int(11) NOT NULL DEFAULT '0',
 `number_of_negatives` int(11) NOT NULL DEFAULT '0',
 `number_of_votes` int(11) NOT NULL DEFAULT '0',
 `number_of_suggestions` int(11) NOT NULL DEFAULT '0',
 `last_suggestion_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
 PRIMARY KEY (`id`),
 UNIQUE KEY `lui` (`link_unique_index`) USING BTREE,
 KEY `st` (`source`,`time_added`),
 KEY `snv` (`source`,`number_of_views`),
 KEY `snvo` (`source`,`number_of_votes`),
 KEY `sct` (`source`,`category`,`time_added`),
 KEY `scnv` (`source`,`category`,`number_of_views`),
 KEY `scnvo` (`source`,`category`,`number_of_votes`),
 KEY `ct` (`category`,`time_added`),
 KEY `cnv` (`category`,`number_of_views`),
 KEY `cnvo` (`category`,`number_of_votes`),
 KEY `hicdnv` (`has_image`,`category`,`date_added`,`number_of_views`),
 KEY `sdnv` (`source`,`date_added`,`number_of_views`),
 KEY `sdnvo` (`source`,`date_added`,`number_of_votes`),
 KEY `scdnv` (`source`,`category`,`date_added`,`number_of_views`),
 KEY `scdnvo` (`source`,`category`,`date_added`,`number_of_votes`),
 KEY `cdnv` (`category`,`date_added`,`number_of_views`),
 KEY `cdnvo` (`category`,`date_added`,`number_of_votes`),
 KEY `dnv` (`date_added`,`number_of_views`),
 KEY `dnvo` (`date_added`,`number_of_votes`),
 KEY `clst` (`category`,`last_suggestion_time`) USING BTREE,
 KEY `slst` (`source`,`last_suggestion_time`) USING BTREE,
 KEY `nv` (`number_of_views`) USING BTREE,
 KEY `nvo` (`number_of_votes`) USING BTREE,
 KEY `t` (`time_added`) USING BTREE,
 KEY `lvt` (`last_view_time`) USING BTREE,
 FULLTEXT KEY `title_content` (`title`,`content`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin ROW_FORMAT=COMPACT

【问题讨论】:

添加SHOW CREATE TABLE News的输出以及你使用的MySQL版本。 @WillemRenzema 请看我的编辑。 【参考方案1】:

试试这个,使用我从 Rolando on DBA.stackexchange 找到的提示:

SELECT News.* FROM (
SELECT id FROM News
WHERE MATCH (title, content) AGAINST ('"My Phrase"' in Boolean Mode) 
) matches
INNER JOIN News
ON News.id = matches.id
ORDER BY News.id DESC
limit 40

【讨论】:

如果目标是避免“文件排序”,我怀疑这会失败。它可能还说“使用临时”。让我们看看EXPLAINs。 OTOH,此查询提供的只是查看* 40 次,而不是“与MATCH 所需的一样多。所以它可能会更快。 正如@RickJames 所说,它是Using temporary; Using filesort。我不知道它是否像我能得到的一样快。这对于小桌子来说是一种很好的方法,但我不推荐它用于大桌子。很明显,对于最常用的短语,它会变慢。在我终止该过程之前,我的一个带有此类短语的测试用例花了 300 秒。我会投票赞成你的答案,但我不能因为我的声誉。 @Mowji 我链接了 dba.stackexchange.com 上的那篇帖子链接的其他一些技巧。我建议通过他们并给他们一个机会。特别是,看看切换到只使用普通索引和 LIKE 是否会提高性能。在使用临时结束时;使用文件排序并不一定意味着查询会执行缓慢,所以更多地关注实际性能,而不是简单地从解释计划中删除它。 @Mowji - 300 秒 - 是否匹配不到 40 行?还是结束? @WillemRenzema 我会测试其他方法,我会告诉你的。

以上是关于快速全文搜索在 MySQL 中按 id 排序的精确短语的一次出现的主要内容,如果未能解决你的问题,请参考以下文章

Go Elasticsearch 查询快速入门

Mysql全文搜索,自然语言模式:按“亲密度”排序

mysql全文搜索,多个关键词权重排序

MySQL 全文搜索以布尔模式按相关性排序

如何按相关性对MYSQL全文搜索结果进行排序

如何在 MySQL 中按节和小节编号排序?