order by 使查询变慢

Posted

技术标签:

【中文标题】order by 使查询变慢【英文标题】:order by makes query slow 【发布时间】:2015-08-22 14:14:20 【问题描述】:

我有两张桌子:

video (ID, TITLE, ..., UPLOADED_DATE)
join_video_category (ID (not used), ID_VIDEO_ ID_CATEGORY)

视频中的行数:4 500 000 | join_video_category 中的行数:5 800 000

1 个视频可以有多个类别。

我有一个完美的查询,最多 20 毫秒得到结果:

SELECT * FROM video WHERE ID IN
(SELECT ID_VIDEO FROM join_video_category WHERE ID_CATEGORY=11)
LIMIT 1000;

这个查询需要1000个视频,顺序不重要。

但是,当我想从一个类别中获取 10 个最新视频时,我的查询大约需要 30-40 秒:

SELECT * FROM video WHERE ID IN
(SELECT ID_VIDEO FROM join_video_category WHERE ID_CATEGORY=11)
ORDER BY UPLOADED_DATE DESC LIMIT 10;

我有 ID_CATEGORY、ID_VIDEO、UPLOADED_DATE、PRIMARY ON ID 视频和 join_video_category 的索引。

我在查询中使用 JOIN 对其进行了测试,结果相同。

【问题讨论】:

【参考方案1】:

首先,比较的是两个非常不同的查询。第一个在遇到它们时会返回一堆视频。第二个必须阅读所有视频,然后对它们进行排序。

尝试将其重写为JOIN

SELECT v.*
FROM video v JOIN
     join_video_category vc
     ON v.id = bc.id_video
WHERE vc.ID_CATEGORY = 11
ORDER BY v.UPLOADED_DATE DESC
LIMIT 10;

这可能有帮助,也可能没有帮助。你有很多数据,所以你可能有很多给定类别的视频。如果是这样,获取最新数据的where 子句可能真的有帮助:

SELECT v.*
FROM video v JOIN
     join_video_category vc
     ON v.id = bc.id_video
WHERE vc.ID_CATEGORY = 11 AND v.UPLOADED_DATE >= '2015-01-01'
ORDER BY v.UPLOADED_DATE DESC
LIMIT 10;

最后,如果这不起作用,请考虑将 UPLOADED_DATE 之类的内容添加到 join_video_category 中。然后,这个查询应该会大火:

select vc.video_id
from join_vdeo_category vc
where vc.ID_CATEGORY = 11 
order by vc.UPLOADED_DATE desc
limit 10;

join_video_category(id_category, uploaded_date, video_id) 上有一个索引。

【讨论】:

好的,我选择将 UPLOADED_DATE 添加到 join_video_category 表中,感谢您的帮助!【参考方案2】:

解决方案#1: 将“in”替换为“exists”会提高性能,请尝试以下查询。

SELECT * FROM video WHERE exists
(SELECT * FROM join_video_category WHERE ID_CATEGORY=11 AND join_video_category.ID_VIDEO = video.ID)
ORDER BY UPLOADED_DATE DESC LIMIT 10;

解决方案 #2:

1) 创建 tem_table

CREATE TABLE TEMP_TABLE AS SELECT * FROM join_video_category WHERE ID_CATEGORY=11;

2) 在解决方案 #1 中使用临时表

SELECT * FROM video WHERE exists
    (SELECT * FROM temp_table WHERE temp_table.ID_VIDEO = video.ID)
    ORDER BY UPLOADED_DATE DESC LIMIT 10;

祝你好运!!

【讨论】:

【参考方案3】:

如果是 1:Many,请不要在 Video 和 Category 之间使用额外的表格。但是,您的行数意味着它是 Many:Many。

如果是1:Many,只需在Video表中有category_id,然后简化所有查询。

如果是Many:Many,那么一定要为联结表使用这个模式:

CREATE TABLE map_video_category (
    video_id ...,
    category_id ...,
    PRIMARY KEY(video_id, category_id),  -- both ids, one direction
    INDEX      (category_id, video_id)   -- both ids, the other direction
) ENGINE=InnoDB;  -- significantly better than MyISAM on INDEX handling here

你提到的ID是浪费。复合键在所有情况下都是最佳的,并且会在大多数情况下提高性能。

不要使用IN ( SELECT ... );优化器在优化它方面做得很差。更改为 JOINLEFT JOINEXISTS 或其他一些构造。

【讨论】:

以上是关于order by 使查询变慢的主要内容,如果未能解决你的问题,请参考以下文章

时间戳字段上的 ORDER BY 使我的查询变慢。如何优化它?

mysql使用order by排序查询导致sql变慢

sql ORDER BY 多个字段,排序变慢几十倍,求解?

如何使 JOIN 查询中的 ORDER BY 更快?我没有尝试过

使用 group by 聚合计数 > 100 万用户 的 Mysql 查询性能变慢

查询优化--ORDER BY查询优化