MySQL 查询在迁移到 RDS 后停留在“发送数据”中 30 秒

Posted

技术标签:

【中文标题】MySQL 查询在迁移到 RDS 后停留在“发送数据”中 30 秒【英文标题】:MySQL queries stuck in "sending data" for 30 seconds after migrating to RDS 【发布时间】:2017-11-22 22:22:57 【问题描述】:

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

【问题讨论】:

欢迎来到 Stack Overflow!您可能没有意识到,当您提出查询优化问题时,最好将SHOW CREATE TABLE 的输出包含在查询中的所有表中。这将帮助回答您问题的人了解您使用了哪些数据类型、索引和约束。帮助我们帮助您! 您好 Bill,SHOW CREATE TABLE 查询出错。 #1064. 您是否将表名作为参数提供?见dev.mysql.com/doc/refman/5.7/en/show-create-table.html 提供表名时,不会报错,但在phpmyadmin中也不会提供结果。 follows_project_user_id 在哪个表中? 【参考方案1】:

EXPLAIN 显示了几个表扫描 (type: ALL),因此它需要超过 30 秒也就不足为奇了。

这是您的解释:

注意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 显示您的查询使用临时表和文件排序。您应该重新考虑是否需要 GROUP BY,因为这可能会导致额外的工作。

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

【讨论】:

我运行了上面的查询,但什么也没发生。没有绿色或红色。 我对以下类似查询有同样的问题: 当你说绿色或红色时,我不知道你在说什么。我不使用 MySQL 的 GUI 工具。你是说ADD INDEX 没有执行? 我正在使用 phpymyadmin,所以我指的是查询处理或错误处理。您的查询建议没有说它做了任何事情,再次运行它不会给出错误,说它已经添加了索引。 需要明确的是,运行查询后 EXPLAIN 中没有键。应该有吗?【参考方案2】:

chv_images 中有索引吗?

我提议:

CREATE INDEX idx_image_id ON chv_images (image_id); 

【讨论】:

我运行了这个查询,但对 EXPLAIN 没有明显的变化。【参考方案3】:

(Bill 的想法很好。我会换个方式讨论……)

Explode-Implode --如果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。这将避免代价高昂的“explode-implode”,其中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

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

【讨论】:

以上是关于MySQL 查询在迁移到 RDS 后停留在“发送数据”中 30 秒的主要内容,如果未能解决你的问题,请参考以下文章

迁移 RDS for MySQL 数据到本地 MySQL

RDS MySQL云原生中间件在网易的实践-3306π南京站

迁移 RDS for MySQL 数据到本地 MySQL

使用 Ruby on rails Elastic Beanstalk 将 RDS 迁移到 Aurora MySQL

AWS RDS Mysql 跨账号迁移

如何将自建库迁移到阿里云RDS