当我们使用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在标签字段上添加全文索引。
我们有太多列吗?索引太多了?行太多了? 这是一个糟糕的疑问吗?
非常感谢您花时间帮助我们!
编辑:添加解释
| 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 | |
答案 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 ...
如果在大量数据上完成,则可能是非常昂贵的操作。但是如果你可以安排在最少的列选择上完成它,并且这些列被正确索引,那么它可以非常快。