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

时间:2016-08-19 22:02:04

标签: mysql sql database query-optimization

我正在研究存储“类似Facebook”图像的MySQL数据库:每个用户都跟随一组“艺术家”,这些艺术家上传图像。

我写了一个查询,对于给定的用户,返回他所遵循的所有艺术家的图像,这些图像是最近30天,按日期排序:

SELECT img.id, img.url, a.name

FROM (SELECT artist FROM user_follow_artist WHERE user = <USER_ID>) AS f -- need to work only on the followed artists
JOIN artistimage AS img ON img.artist = f.artist -- join on the table that contains the images (8.000.000 rows!)
JOIN artist AS a ON a.id = img.artist -- join on artist table to add details on image's owner

-- following: where + orderby that slow down everything
WHERE img.uploadDate > NOW() - INTERVAL 30 DAY 
ORDER BY img.uploadDate DESC 
LIMIT 50

查询平均花费30秒,用户跟随艺术家的次数越多,所需的时间就越多。 以下是一些细节:

  • 如上所述,artistimage表是800万行
  • 如果我删除了ORDER BY子句,则时间会减少到2-3秒
  • img.uploadDate是一个TIMESTAMP,它是INDEXED
  • 引擎是InnoDB

我希望索引uploadDate可以提高速度,但没有任何改变。我该如何解决这个问题?

编辑:表格结构

artist
------------------
id (integer, primary)
name (string)

user_follow_artist
------------------
user (integer, foreign key on user.id, indexed)
artist (integer, foreign key on artist.id, indexed)

artistimage
------------------
id (integer, primary)
artist (integer, foreign key on artist.id, indexed)
url (string)
uploadDate (timestamp, indexed)

说明:

id      select_type     table               type        possible_keys       key         key_len     ref             rows    Extra
1       PRIMARY         <derived2>          ALL         NULL                NULL        NULL        NULL            327     Using temporary; Using filesort
1       PRIMARY         img                 ref         artist,uploadDate   artist      9           img.artist      36      Using where
1       PRIMARY         user                eq_ref      PRIMARY             PRIMARY     8           db.img.artist   1    
2       DERIVED         user_follow_artist  ref         PRIMARY,user        user        8                           327     Using index

如果我删除了ORDER BY,那么EXPLAIN是相同的,但第一行没有Using temporary; Using filesort

4 个答案:

答案 0 :(得分:1)

我有一个类似的问题,使用此查询

SELECT * FROM tab_pag
WHERE pag_cod_pon=?
ORDER BY pag_tms DESC 
LIMIT 1

问题是mysql将时间戳存储为32位整数,但在这种情况下处理就像#34;格式化完整日期&#34;,因此从int转换为日期需要花费大量时间。 我解决了以自然的方式强迫排序&#34;使用unix_timestamp

SELECT * FROM tab_pag
WHERE pag_cod_pon=?
ORDER BY <b>unix_timestamp(pag_tms)</b> DESC 
LIMIT 1

这将我的查询从500毫秒减少到50毫秒......

答案 1 :(得分:0)

我认为你的“限制50”令你感到困惑。它为您提供了没有标准的前50行,它们总是快速返回。 当你添加where和order by子句时,它将要求MySQL获取所有行,然后排序和过滤以获得结果。

我认为如果你拿出了WHERE,ORDER LIMIT 50,你会看到你的查询需要很长时间才能获取所有行。

为了加快速度,您可以尝试重新编写查询 您可以为“(SELECT artist FROM user_follow_artist WHERE user =)”设置临时表。

您可以将WHERE子句移动到JOIN,如下所示:

JOIN artistimage AS img ON img.artist = f.artist AND img.uploadDate > NOW() - INTERVAL 30 DAY

答案 2 :(得分:0)

您不需要user_follow_artist的子查询,该表应按原样加入。尽管如此,问题是查询未使用uploadDate中的索引。试试这个:

SELECT
    img.id,
    img.url,
    a.name
FROM
    user_follow_artist AS f
    INNER JOIN
    (
        SELECT id, url, uploadDate
        FROM artistimage
        WHERE uploadDate > NOW() - INTERVAL 30 DAY
    ) img ON ( img.id = f.artist )
    INNER JOIN artist AS a ON ( a.id = img.id )
WHERE
    f.user = <USER_ID>
ORDER BY
    img.uploadDate DESC

如果子查询没有返回太多结果,那么这应该可以正常工作。

答案 3 :(得分:0)

首先,让我们简化查询,因为IN ( SELECT ... )不能很好地优化:

SELECT  img.id, img.url, a.name
    FROM  user_follow_artist AS f
    JOIN  artistimage AS img  ON img.artist = f.artist
    JOIN  artist AS a  ON a.id = img.artist
    WHERE  img.uploadDate > NOW() - INTERVAL 30 DAY
      AND  f.user = <user_id>
    ORDER BY  img.uploadDate DESC
    LIMIT  50

然后让我们获得最好的索引。但是,唉,你正在过滤一张桌子并订购和限制另一张桌子。所以,让我们添加一些潜在有用的索引并希望最好:

如果优化程序以WHERE f.user...

开头,请为其提供帮助
f: INDEX(user, artist)
img: INDEX(artist, uploadDate)

如果优化程序以WHERE img.uploadDate... ORDER BY...

开头,请为其提供帮助
img: INDEX(uploadDate)
f: INDEX(artist, user)

请注意,其中大多数是&#34;复合&#34;索引和列的顺序是至关重要的。

我更希望看到SHOW CREATE TABLE而不是你提供的散文。

如果user_follow_artist是多个:多个映射表,我强烈建议您按照here中的提示进行操作。