使用FULLTEXT从大表中检索排名靠前的行非常慢

时间:2014-05-18 00:05:02

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

当我们使用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)

这些查询太慢了。有没有办法改善这个?

'广告'表包含2百万行,触发器设置为将数据复制到搜索标题中。搜索标题包含广告中每行的ID,标题和标签。 表'广告'由innoDB和' searchs_titles'提供支持。通过myISAM在标签字段上添加全文索引。

我们有太多列吗?索引太多了?行太多了? 这是一个糟糕的疑问吗?

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

enter image description here

enter image description here

编辑:添加解释

| 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 |                                              |

1 个答案:

答案 0 :(得分:2)

专业提示:切勿在生产软件的*语句中使用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 ... datashuffling操作。这应该可以更快地检索适当的id值。然后外部查询获取适当的行。

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