我正在研究存储“类似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秒,用户跟随艺术家的次数越多,所需的时间就越多。 以下是一些细节:
我希望索引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
。
答案 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中的提示进行操作。