当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
答案 0 :(得分:0)
chv_images中有没有索引?
我建议:
"034545104X";"0155061224";"0446520802"
"276725" "0"
"276726" "5"
"276727" "0"
答案 1 :(得分:0)
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显示您的查询使用临时表和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 BY
和LIMIT
。这种一般模式:
( SELECT ... ORDER BY ... LIMIT 15 )
UNION
( SELECT ... ORDER BY ... LIMIT 15 )
ORDER BY ... LIMIT 15
是的,重复ORDER BY
和LIMIT
。
适用于第1页。如果您想要接下来的15行,请参阅http://mysql.rjweb.org/doc.php/pagination#pagination_and_union
在构建这两个子选择之后,看看它们;我认为你将能够优化每一个,并且可能需要新的索引,因为优化器将以不同的“第一”表开始。