【问题标题】:retrieving top-ranking rows from large tables using FULLTEXT is very slow使用 FULLTEXT 从大表中检索排名靠前的行非常慢
【发布时间】:2014-07-06 04:58:50
【问题描述】:

当我们使用 mysql-client 登录我们的数据库并启动这些查询时:

第一个测试查询:

select a.* 
  from ads a  
 inner join searchs_titles s on s.id_ad = a.id 
 where match(s.label) against ('"bmw serie 3"' in boolean mode) 
 order by a.ranking asc limit 0, 10;

结果是:

10 rows in set (1 min 5.37 sec)

第二次测试查询:

select a.*
  from ads a  
 inner join searchs_titles s on s.id_ad = a.id 
 where match(s.label) against ('"ford mondeo"' in boolean mode) 
 order by a.ranking asc limit 0, 10;

结果是:

10 rows in set (2 min 13.88 sec)

这些查询太慢了。有什么办法可以改善吗?

“广告”表包含 200 万行,触发器设置为将数据复制到搜索标题中。搜索标题包含广告中每一行的 id、标题和标签。 表 'ads' 由 innoDB 提供支持,而 myISAM 为 'searchs_titles' 提供支持,标签字段上有全文索引。

我们的列太多了吗?索引太多?行数太多? 这是一个错误的查询吗?

非常感谢您花时间帮助我们!

编辑:添加解释

| id | select_type | table | type     | possible_keys        | key     | key_len | ref              | rows | Extra                                        |
|  1 | SIMPLE      | s     | fulltext | id_ad,label          | label   | 0       |                  |    1 | Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | a     | eq_ref   | PRIMARY,id,id_2,id_3 | PRIMARY | 4       | XXXXXX.s.id_ad |    1 |                                              |

【问题讨论】:

  • 你的意思是我们应该请求 a.the specifics indexed columns 而不是请求 a.*?
  • 截图不可读。你有关于 searchs_titles.label 的全文索引吗?
  • 你能给出查询的解释计划吗?由于这是一个优化问题,因此此信息很重要。

标签: mysql indexing full-text-search sql-order-by query-optimization


【解决方案1】:

专业提示:切勿在生产软件中的SELECT 语句中使用*(除非您有充分的理由)。通过询问所有列,您拒绝优化器访问有关如何最好地利用您的索引的信息。

观察:您通过ads.ranking 订购并获得十个结果。但是ads.ranking 的基数非常低——根据您问题中的图像,它有 26 个不同的值。您的查询是否正常工作?

观察:您说过搜索的全文部分需要 0.77 秒。我的意思是这部分:

select s.id 
  from searchs_titles AS s
 where match(s.label) against ('"ford mondeo"' in boolean mode) 

这很好。这意味着我们可以专注于查询的其余部分。

您还说您一直在关闭对表格的插入进行测试。这很好,因为它排除了争用导致查询缓慢的原因。

建议:为ads创建一个合适的复合索引。对于您当前的查询,请尝试在(id, ranking) 上建立索引,这可能会让您的ORDER BY 操作避免全表扫描。

然后,尝试此查询以提取您需要的十个 a.id 值的集合,然后检索数据行。这将利用您的复合索引。

select z.*  
  from ads AS z
  join ( select a.id, a.ranking
           from ads AS a
          inner join searchs_titles s on s.id_ad = a.id 
          where match(s.label) against ('"ford mondeo"' in boolean mode) 
          order by a.ranking asc 
          limit 0, 10
        ) AS b ON z.id = b.id
 order by z.ranking

这使用子查询对一小部分列进行order by ... limit ... 数据混洗操作。这应该可以更快地检索适当的 id 值。然后外部查询获取适当的行。

底线是:ORDER BY ... LIMIT ... 在处理大量数据时可能是一项非常昂贵的操作。但是,如果您可以安排在最少的列选择上完成,并且这些列被正确索引,那么它可以非常快。

【讨论】:

  • 感谢您与我们分享您的经验和知识,我们在这里都是初级 :),回答您的第一个问题需要 0.77 秒不同的关键字未缓存我认为它很快!第二个问题:目前没有加载到这个数据库中,我们已经停止了工作人员向其中插入/更新数据。我们应该使用我们的副本只选择并使用我们的主节点进行插入/更新吗?再次感谢我会尝试您的建议并随时通知您
  • ads 上尝试复合索引。并尝试我的嵌套查询,从复合索引中获取 ads.id 值,然后在主表中仅找到 10 行。全文搜索 0.77 秒真的很不错。
  • 在您修复此 > 60 秒查询后,让我们解决副本问题。
  • 非常感谢!现在查询真的很快,不到0.50!我用 sync && echo 3 | 重置缓存sudo tee /proc/sys/vm/drop_caches && 重置查询缓存以确保!再次感谢!
  • 太好了!为了他人的利益,你到底做了什么?检索 ID 的子查询?复合指数?请告诉我们。
猜你喜欢
  • 2023-03-03
  • 2016-06-05
  • 2017-05-15
  • 1970-01-01
  • 2017-12-21
  • 2018-12-05
  • 1970-01-01
  • 2013-06-27
  • 1970-01-01
相关资源
最近更新 更多