全文搜索导致“全文初始化”中花费大量时间


12

我目前正在尝试对Stack Overflow注释的数据转储运行一些查询。架构如下所示:

CREATE TABLE `socomments` (
  `Id` int(11) NOT NULL,
  `PostId` int(11) NOT NULL,
  `Score` int(11) DEFAULT NULL,
  `Text` varchar(600) NOT NULL,
  `CreationDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `UserId` int(11) NOT NULL,
  PRIMARY KEY (`Id`),
  KEY `idx_socomments_PostId` (`PostId`),
  KEY `CreationDate` (`CreationDate`),
  FULLTEXT KEY `Text` (`Text`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

我针对该表运行了该查询,并且运行速度极其慢(它确实具有2900万行,但是具有全文索引):

SELECT *
FROM socomments
WHERE MATCH (Text) AGAINST ('"fixed the post"' IN BOOLEAN MODE)

因此,我对其进行了概要分析,其结果是:

|| Status                     || Duration ||
|| starting                   || 0.000058 ||
|| checking permissions       || 0.000006 ||
|| Opening tables             || 0.000014 ||
|| init                       || 0.000019 ||
|| System lock                || 0.000006 ||
|| optimizing                 || 0.000007 ||
|| statistics                 || 0.000013 ||
|| preparing                  || 0.000005 ||
|| FULLTEXT initialization    || 207.1112 ||
|| executing                  || 0.000009 ||
|| Sending data               || 0.000856 ||
|| end                        || 0.000004 ||
|| query end                  || 0.000004 ||
|| closing tables             || 0.000006 ||
|| freeing items              || 0.000059 ||
|| logging slow query         || 0.000037 ||
|| cleaning up                || 0.000046 ||

如您所见,它在FULLTEXT初始化上花费了很长时间。这正常吗?如果没有,我将如何解决?


想法:建立第二张表,将每1.000条评论放在一个文本字段中。现在,您首先在第二张表中进行搜索,然后获得例如id_group 2id_group 23。这样,您就可以在主表中进行搜索,并将查询范围限制为2.000至2.999和23.000至23.999。当然,当您混合所有注释以创建新的关键字组合时,第二个将根据需要获得更多结果,但最终它会加快整个过程。当然,它会使磁盘空间使用量增加一倍。新评论应保存在组表中。
mgutt 2015年

Answers:


5

其他人发现这种麻烦的情况

由于MySQL文档在此线程状态上非常简洁

FULLTEXT初始化

服务器正在准备执行自然语言的全文本搜索。

您唯一的办法就是用更少的数据做准备。怎么样 ?

建议#1

再次查看您的查询。它正在选择所有列。我将重构查询以仅从中收集id列socomments。然后,将这些检索到的ID重新加入socomments表中。

SELECT B.* FROM
(SELECT id FROM socomments
WHERE MATCH (Text) AGAINST ('"fixed the post"' IN BOOLEAN MODE)) A
LEFT JOIN socomments B USING (id);

这可能会产生较丑陋的EXPLAIN计划,但我认为剖析会有所改善。基本思想是:如果您进行了积极的FULLTEXT搜索,请使其在该FULLTEXT initialization阶段收集的数据量最少,从而减少时间。

我以前推荐过很多次

建议#2

请确保您设置的是基于InnoDB的FULLTEXT选项,而不是MyISAM的选项。您应该关注的两个选项是

考虑一下。文本字段为VARCHAR(600)。说平均为300个字节。您有29,000,000亿。那将是8GB的一点点。也许增加innodb_ft_cache_sizeinnodb_ft_total_cache_size也可能有所帮助。

确保您有足够的RAM用于更大的InnoDB FULLTEXT缓冲区。

试试看 !!!


尝试了这两个建议,将时间减少了大约10秒,降至200秒。奇怪的是,缓冲池的利用率仅为9%...
hichris123

尝试在AGAINST部分中加一个加号:SELECT B.* FROM (SELECT id FROM socomments WHERE MATCH (Text) AGAINST ('+"fixed the post"' IN BOOLEAN MODE)) A LEFT JOIN socomments B USING (id);看看是否有区别。
RolandoMySQLDBA 2014年

我为什么建议加号?Doc(dev.mysql.com/doc/refman/5.6/en/fulltext-boolean.html)说,A leading or trailing plus sign indicates that this word must be present in each row that is returned. InnoDB only supports leading plus signs.在您的特定情况下,fixed the post必须存在确切的短语。
RolandoMySQLDBA 2014年

结果相同。快慢一点,所以可能只是由于执行时间的微小差异。
hichris123

5

如果您使用的是InnoDB FULLTEXT索引,则在查询具有大量已删除行的表时,查询通常会挂在“ FULLTEXT初始化”状态。在InnoDB的FULLTEXT实现中,对受影响的表运行后续的OPTIMIZE操作之前,不会修剪删除的行。参见:https : //dev.mysql.com/doc/refman/5.6/en/innodb-fulltext-index.html

要删除已删除记录的全文索引条目,必须使用innodb_optimize_fulltext_only = ON在索引表上运行OPTIMIZE TABLE,以重建全文索引。

也可以通过查询information_schema.innodb_ft_deleted来检查已删除但未清除的记录数。

要解决此问题,应定期对具有InnoDB FULLTEXT索引的表运行OPTIMIZE TABLE。


我对此有逻辑,但是您可以验证一下innodb_optimize_fulltext_only=1,一个OPTIMIZE表实际上在“等待”中处理了删除的行吗? dba.stackexchange.com/questions/174486/…–
Riedsio


0

MySQL中的全文本索引不旨在支持大量数据,因此随着数据集的增长,搜索速度会迅速下降。解决方案之一是使用外部全文搜索引擎,例如Solr或Sphinx,这些引擎具有改进的搜索功能(相关性调整和短语搜索支持,内置构面,摘要等),扩展了查询语法,并且从中到上的速度更快。 -大数据集。

Solr基于Java平台,因此如果您运行基于Java的应用程序将是您的自然选择,Sphinx是用C ++编写的,并且充当了与MySQL相同的守护程序。在向外部引擎提供要搜索的数据后,您还可以将一些查询移出MySQL。我无法告诉您哪种引擎更好,我主要使用Sphinx,这是用法示例:http : //astellar.com/2011/12/replacing-mysql-full-text-search-with-sphinx/

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.