快速全文搜索在 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
表中获取记录,并在其title
或content
中使用确切的短语:
SELECT * FROM News WHERE MATCH (title, content) AGAINST ('"My Phrase"' in Boolean Mode)
但结果并不令人满意。对我来说,在搜索一个确切的短语时,首先查看该短语的最新消息是合乎逻辑的。我不需要按相关性对结果进行排序。我不介意找到的第一条记录是否有 1 次出现我的短语,而第二条记录有 10 次。我只关心 ORDER BY time_added
或 ORDER 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 排序的精确短语的一次出现的主要内容,如果未能解决你的问题,请参考以下文章