MySQL查询卡在"发送数据"迁移到RDS后30秒

时间:2017-11-22 22:22:57

标签: mysql amazon-web-services relational-database query-optimization innodb

当MySQL与本网站的其余部分位于同一EC2实例上时,此查询(以及其他一些我认为存在相关问题的查询)并不需要30秒。更像是毫秒。

有什么可以看的吗?

SELECT *, chv_images.image_id FROM chv_images
LEFT JOIN chv_storages ON chv_images.image_storage_id = 
chv_storages.storage_id
LEFT JOIN chv_users ON chv_images.image_user_id = chv_users.user_id
LEFT JOIN chv_albums ON chv_images.image_album_id = chv_albums.album_id
LEFT JOIN chv_categories ON chv_images.image_category_id = 
chv_categories.category_id
LEFT JOIN chv_meta ON chv_images.image_id = chv_meta.image_id
LEFT JOIN chv_likes ON chv_likes.like_content_type = "image" AND 
chv_likes.like_content_id = chv_images.image_id AND chv_likes.like_user_id = 1
LEFT JOIN chv_follows ON chv_follows.follow_followed_user_id = 
chv_images.image_user_id
LEFT JOIN chv_follows_projects ON 
chv_follows_projects.follows_project_project_id = 
chv_images.image_project_id LEFT JOIN chv_projects ON 
chv_projects.project_id = follows_project_project_id WHERE 
chv_follows.follow_user_id='1' OR (follows_project_user_id = 1 AND 
chv_projects.project_privacy = "public" AND 
chv_projects.project_is_public_upload = 1)  GROUP BY chv_images.image_id 
ORDER BY chv_images.image_id DESC
LIMIT 0,15

And this is what EXPLAIN shows:

谢谢

更新:此查询具有相同的问题。它没有GROUP BY。

 SELECT *, chv_images.image_id FROM chv_images
 LEFT JOIN chv_storages ON chv_images.image_storage_id = 
 chv_storages.storage_id
 LEFT JOIN chv_users ON chv_images.image_user_id = chv_users.user_id
 LEFT JOIN chv_albums ON chv_images.image_album_id = chv_albums.album_id
 LEFT JOIN chv_categories ON chv_images.image_category_id = 
 chv_categories.category_id
 LEFT JOIN chv_meta ON chv_images.image_id = chv_meta.image_id
 LEFT JOIN chv_likes ON chv_likes.like_content_type = "image" AND 
 chv_likes.like_content_id = chv_images.image_id AND chv_likes.like_user_id = 1

 ORDER BY chv_images.image_id DESC
 LIMIT 0,15

3 个答案:

答案 0 :(得分:0)

chv_images中有没有索引?

我建议:

          "034545104X";"0155061224";"0446520802"
"276725"      "0"
"276726"                   "5"
"276727"                               "0"

答案 1 :(得分:0)

EXPLAIN显示了几个表扫描(type: ALL),所以它花了30多秒就不足为奇了。

这是您的EXPLAIN: enter image description here

请注意,列rows显示从第一个表chv_images中读取的估计14420行。它正在对所有行进行表扫描。

通常,当您执行一系列JOIN时,可以将EXPLAIN的rows列中的所有值组合在一起,最终结果是MySQL必须执行的行读取次数。在这种情况下,它是14420 * 2 * 1 * 1 * 2 * 1 * 916,或 52,834,880 行读取。这应该考虑在同一查询中进行多次表扫描的高成本。

您可以通过在这些表上创建一些索引来帮助避免这些表扫描:

ALTER TABLE chv_storages
  ADD INDEX (storage_id);

ALTER TABLE chv_categories
  ADD INDEX (category_id);

ALTER TABLE chv_likes
  ADD INDEX (like_content_id, like_content_type, like_user_id);

尝试创建这些索引,然后再次运行EXPLAIN。

其他表已经按主键(type: eq_ref)或辅助键(type: ref)进行查找,因此这些表已经过优化。

您的EXPLAIN显示您的查询使用临时表和filesort。您应该重新考虑是否需要GROUP BY,因为这可能会导致额外的工作。

另一个提示是避免使用SELECT *,因为它可能会强制查询读取您不需要的许多额外列。相反,只显示您需要的列。

答案 2 :(得分:0)

(比尔的想法很好。我会以不同的方式讨论......)

爆炸 - 内爆 - 如果LEFT JOINs匹配不超过1行,请更改,例如

SELECT 
    ...
    LEFT JOIN  chv_meta  ON chv_images.image_id = chv_meta.image_id

SELECT ...,
    ( SELECT foo FROM chv_meta WHERE image_id = chv_images.image_id ) AS foo, ...

如果可以对所有 JOINs执行此操作,则可以删除GROUP BY。这将避免代价高昂的“爆炸内爆”,其中JOINs导致更多行,然后GROUP BY摆脱重复。 (我怀疑你无法移动所有连接。)

OR - > UNION - OR很难优化。您的查询看起来很适合转换为UNION,然后生成更多有用的索引。

    WHERE  chv_follows.follow_user_id='1'
      OR  (follows_project_user_id = 1
              AND  chv_projects.project_privacy = "public"
              AND  chv_projects.project_is_public_upload = 1
          )

假设follows_project_user_id位于`chv_images,

( SELECT ...
    WHERE chv_follows.follow_user_id='1' )
UNION DISTINCT   -- or ALL, if you are sure there won't be dups
( SELECT ...
    WHERE follows_project_user_id = 1
      AND  chv_projects.project_privacy = "public"
      AND  chv_projects.project_is_public_upload = 1 )

所需索引:

chv_follows:  (follow_user_id)
chv_projects: (project_privacy, project_is_public_upload) -- either order

但尚未处理ORDER BYLIMIT。这种一般模式:

( SELECT ... ORDER BY ... LIMIT 15 )
UNION
( SELECT ... ORDER BY ... LIMIT 15 )
ORDER BY ... LIMIT 15

是的,重复ORDER BYLIMIT

适用于第1页。如果您想要接下来的15行,请参阅http://mysql.rjweb.org/doc.php/pagination#pagination_and_union

在构建这两个子选择之后,看看它们;我认为你将能够优化每一个,并且可能需要新的索引,因为优化器将以不同的“第一”表开始。