4

使用ORDER BY运行此程序需要 10 多秒,最终导致我的网站在高流量时崩溃。

select *
from tbluserinfluences, tblcontent, tblusers
where tblcontent.userid = tblusers.id
and tbluserinfluences.userid = tblusers.id
and tbluserinfluences.lcase_influence = 'pink floyd'
order by tblcontent.score desc
limit 0, 160

在没有 ORDER BY的情况下运行相同的查询只需几毫秒。

select *
from tbluserinfluences, tblcontent, tblusers
where tblcontent.userid = tblusers.id
and tbluserinfluences.userid = tblusers.id
and tbluserinfluences.lcase_influence = 'pink floyd'
order by tblcontent.score desc
limit 0, 160

这是解释

在此处输入图像描述

有任何想法吗?我愿意将其拆分为多个查询、创建临时表或任何其他有帮助的东西。这个查询让我(和我的用户)感到厌烦。

谢谢!

4

7 回答 7

7

您可能需要在 score 列上建立索引。

于 2012-05-29T23:19:36.853 回答
3

好的,首先要做的事情:LIMIT 隐藏了大量错误查询,直到有人添加 ORDER BY - LIMIT 邀请数据库引擎在生成指定数量的记录后立即取消查询,但尽快添加 ORDER BY 时,所有记录都是在内部生成的,但对程序员隐藏 - 如果一个 LIMIT 的查询因 ORDER BY 而大大减慢,那么它不是一个好的查询开始。

也就是说,您需要对您的查询(和数据库设置)进行一些小的更改以改进事情。通过查看 EXPLAIN 计划(包括此计划,您在前 10% 中),有很多事情很突出 - 结果集中有 240,000 条记录正在排序。从“使用文件排序”看来,发生了 2 次排序阶段,加上查询正在创建一个临时表 - 我会考虑增加你的sort_buffer_size,但要小心不要让它太大,因为我似乎记得它是每个会话不是全局缓冲区,因此如果您有 100 个并发会话,请不要将其设置为 256MB - 我猜 4MB 或 8MB 可能是不错的起始位置。

如果这不能大大改善事情,我会开始处理查询本身:EXPLAIN 输出告诉我们lcase_influence索引有 300+ 字节键 - 如果你将影响字符串移到一个单独的tblInfluence,并且只包含tblInfluence.idtbluserinfluences表中,并将其编入索引,然后您将同时删除tbluserinfluences表的大小和影响名称索引。

如果这不能解决问题,那么我会考虑移动排序,以便它只对所需的最小字段进行排序,而不是对整个输出记录进行排序。我也将tblUsrContent直接加入tblUserInfluences- 我怀疑它不会有太大的不同,但如果它是我的代码,我更喜欢单步连接而不是可能的长连接链。

于 2012-05-31T04:03:58.957 回答
1

好的,这是一个巨大的 hack,但我想出了一个(临时)解决问题的方法。

只有在搜索“pink floyd”到“coldplay”等非常流行的乐队时,查询才会变慢。任何不那么受欢迎的乐队,查询都很快。

通过一些反复试验,我发现如果我强制查询使用tblcontent.score索引,对于像“pink floyd”这样的流行乐队来说它是超快的,但对于像“romantics”这样不那么流行的乐队来说就慢了。

Hacky 解决方案:前 100 个乐队的强制分数指数。让 MySql 对所有其他波段使用其默认值。叹。

因此,粉红弗洛伊德查询的快速版本是:

select *
from tbluserinfluences, tblcontent FORCE INDEX(score), tblusers
where tblcontent.userid = tblusers.id
and tbluserinfluences.userid = tblusers.id
and tbluserinfluences.lcase_influence = 'pink floyd'
order by tblcontent.score desc
limit 0, 160

浪漫主义(不太流行)查询的快速版本是:

select *
from tbluserinfluences, tblcontent, tblusers
where tblcontent.userid = tblusers.id
and tbluserinfluences.userid = tblusers.id
and tbluserinfluences.lcase_influence = 'pink floyd'
order by tblcontent.score desc
limit 0, 160

当我在 Defcon 5 时,这是一个不错的解决方案。稍后我会想出一些更优雅的东西。

于 2012-05-29T23:54:52.187 回答
0

在此处更改my.ini文件:innodb_buffer_pool_size = 300M - 并根据您的 pc 或服务器中的可用内存更改大小。对我来说有效!

于 2013-04-22T13:51:17.693 回答
0

在没有看到您的架构的情况下,我会在 SCORE 字段上添加一个索引。对于任何索引,INSERT 的性能都会受到轻微影响,但听起来选择查询对您来说是最重要的部分。

于 2012-05-29T23:24:11.247 回答
0

尝试这个

select 
ui.*,
tc.*
tu.*
from tbluserinfluences as ui
LEFT JOIN tblusers AS tu ON tu.id = ui.userid
LEFT JOIN tblcontent AS tc ON tc.userid = tu.id
where ui.lcase_influence = 'pink floyd'
order by ???.score desc
limit 0, 160

代替 ???与有关表。我不能尝试,但我会从那个开始。

于 2012-05-29T23:28:53.840 回答
0

我就是这样做的

select * from (
 select *
 from tbluserinfluences, tblcontent FORCE INDEX(score), tblusers
 where tblcontent.userid = tblusers.id
 and tbluserinfluences.userid = tblusers.id
 and tbluserinfluences.lcase_influence = 'pink floyd'
 limit 0, 160) tbl
order by tbl.score desc

先限制再对160条记录排序,而不是先排序再限制

于 2013-07-16T07:25:07.453 回答