简体   繁体   English

使用多个计算、连接和排序来优化/替代慢速 MySQL 查询

[英]Optimizing/Alternative to slow MySQL query with multiple calculations, joins and order bys

I have a complex MySQL query which contains multiple calculations and joins to retrieve a list of contractors by location.我有一个复杂的 MySQL 查询,其中包含多个计算和连接,以按位置检索承包商列表。 The contractors table (users) contains 100,000+ rows and growing.承包商表(用户)包含 100,000 多行并且还在增长。 The issue I'm having is that the query takes over 1.5 seconds to execute which is causing a significant delay to the page load.我遇到的问题是查询需要超过 1.5 秒才能执行,这导致页面加载显着延迟。

I have found that removing the ORDER BY clause, the speed is increased significantly (< 0.05s).我发现删除 ORDER BY 子句后,速度显着提高(< 0.05s)。 After looking through other related questions on Stack Overflow, I understand why this is the case but have not yet found a viable solution.在查看了有关 Stack Overflow 的其他相关问题后,我明白为什么会出现这种情况,但还没有找到可行的解决方案。

It's also worth noting I have added indexes as suggested in other posts but I believe you cannot optimize any further when sorting on calculated columns.还值得注意的是,我已按照其他帖子中的建议添加了索引,但我相信在对计算列进行排序时无法进一步优化。 (Please correct me if I'm wrong) (如果我错了请纠正我)

Here is the query (I have removed several columns and joins for simplicity but still the query takes the same time to execute):这是查询(为简单起见,我删除了几列和连接,但查询仍然需要相同的时间来执行):

SELECT `users`.`id`,
       `users`.`username`,
       IF (Max(up.premium_expires_at) > Now(), 1, 0) AS `is_premium`,
       IF (users.last_online_at >= Now() - INTERVAL 30 day, 1, 0) AS `recent_login`,
       IF (da.id IS NOT NULL, 1, 0) AS `is_available`,
       ( 3959 * Acos(Cos(Radians(53.80592)) * Cos(Radians(lat)) * Cos(
                              Radians(lng) - Radians(-1.53834
                                                  )) + Sin(Radians(53.80592)) *
                                                       Sin(Radians(lat))) ) AS
        `distance`
FROM   `users`
       INNER JOIN `users_places` AS `up`
               ON `users`.`id` = `up`.`user_id`
       INNER JOIN `places` AS `mp`
               ON `users`.`place_id` = `mp`.`id`
       LEFT JOIN `users_dates_available` AS `da`
              ON `da`.`user_id` = `users`.`id`
                 AND `from` <= Curdate()
                 AND `to` >= Curdate()
       LEFT JOIN (SELECT user_id,
                         Sum(score) AS score
                  FROM   users_feedback
                  WHERE  status = 1
                  GROUP  BY user_id) AS feedback
              ON `users`.`id` = `feedback`.`user_id`
WHERE  `users`.`status` = 1
       AND `users`.`approved` = 1
GROUP  BY `users`.`id`
HAVING `distance` < 50
ORDER  BY `is_premium` DESC,
          `recent_login` DESC
LIMIT  5 

And here's the results of EXPLAIN这是 EXPLAIN 的结果

解释

So I guess my question is: What is the quickest way to display this data on a web page?所以我想我的问题是:在 web 页面上显示这些数据的最快方法是什么?

What I've tried:我试过的:

  1. The query is part of a Laravel application.该查询是 Laravel 应用程序的一部分。 I've tried running the query without the ORDER BY and sorting by PHP.我尝试在没有 ORDER BY 的情况下运行查询并按 PHP 排序。 However execution times remain slow.但是执行时间仍然很慢。

  2. Running the query without LEFT joins and I noticed significant improvements to speed.在没有 LEFT 连接的情况下运行查询,我注意到速度有了显着提高。 However the query must use LEFT joins for the calculations in the SELECT criteria (we're checking for NULL values).但是,查询必须使用左连接来计算 SELECT 标准(我们正在检查 NULL 值)。

  3. Using Views - still same query speeds with a pre-compiled view.使用视图 - 使用预编译视图的查询速度仍然相同。

The only other option I can think of is to create a temporary table which contains all the calculated fields and query this.我能想到的唯一其他选择是创建一个包含所有计算字段的临时表并查询它。 However this will not store the 'distance' column as this is specific to the user running the query and I will still be sorting by a calculated column.但是,这不会存储“距离”列,因为这是特定于运行查询的用户,我仍将按计算列排序。

Is there another option or another way to optimize this query that I'm missing?是否有另一种选择或另一种方法来优化我缺少的这个查询? Thanks谢谢

The query seems not to use feedback , so remove the LEFT JOIN .该查询似乎没有使用feedback ,因此删除了LEFT JOIN That will save on some wasted effort.这将节省一些浪费的精力。

Similarly, places seems to be useless, except as an existence test.同样, places似乎也没什么用,除了作为存在测试。

Which table are lat and lng in? latlng在哪个表? (I cannot finish my analysis without knowing what table each column is in.) (如果不知道列在哪个表中,我无法完成我的分析。)

Are from and to of datatype DATE ? fromto的数据类型是DATE吗? If so, the WHERE clause involving them seems to say "anytime today".如果是这样,涉及它们的WHERE子句似乎是“今天任何时候”。 Is that correct?那是对的吗?

Get some of that cleaned up.把其中的一些清理干净。 After that, I may be able to suggest moving one of the Joins until after the GROUP BY and LIMIT .之后,我可能会建议将其中一个联接移动到GROUP BYLIMIT之后。 Or maybe the GROUP BY can be eliminated.或者也许可以消除GROUP BY

Some indexes that might be useful:一些可能有用的索引:

users:  INDEX(status, approved, id,  username, last_online_at, place_id)
up:  INDEX(user_id,  premium_expires_at)
da:  INDEX(user_id,  id)
users_feedback:  INDEX(status, user_id)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM