为啥这个 pg 查询这么慢?我怎样才能让它更快?
Posted
技术标签:
【中文标题】为啥这个 pg 查询这么慢?我怎样才能让它更快?【英文标题】:Why is this pg query so slow? How can I make it faster?为什么这个 pg 查询这么慢?我怎样才能让它更快? 【发布时间】:2019-09-28 04:51:03 【问题描述】:这是查询:
(SELECT *
FROM url
WHERE domain = 'youtube.com'
AND timestamp > NOW() - INTERVAL '24 hours'
ORDER BY likes DESC LIMIT 10)
UNION
(SELECT *
FROM url
WHERE domain = 'twitter.com'
AND timestamp > NOW() - INTERVAL '24 hours'
ORDER BY likes DESC LIMIT 10)
UNION
(SELECT *
FROM url
WHERE domain = 'reddit.com'
AND timestamp > NOW() - INTERVAL '24 hours'
ORDER BY likes DESC LIMIT 10)
ORDER BY timestamp DESC
这是EXPLAIN ANALYZE
。
Sort (cost=20460.17..20460.25 rows=30 width=497) (actual time=5161.013..5161.015 rows=30 loops=1)
Sort Key: url."timestamp" DESC
Sort Method: quicksort Memory: 53kB
-> HashAggregate (cost=20459.14..20459.44 rows=30 width=497) (actual time=5160.709..5160.738 rows=30 loops=1)
Group Key: url.url, url.domain, url.title, url.views, url.likes, url.dislikes, url.comments, url.shares, url.links_to_url, url."user", url.thumbnail_url, url.is_collection, url.image_url, url.video_url, url.audio_url, url.width, url.height, url.body, url.source, url."timestamp", url.created_at, url.updated_at, url.duration_seconds, url.tags, url.channel
-> Append (cost=0.43..20457.26 rows=30 width=497) (actual time=0.514..5160.073 rows=30 loops=1)
-> Limit (cost=0.43..18150.71 rows=10 width=1177) (actual time=0.513..28.599 rows=10 loops=1)
-> Index Scan Backward using "url-likes-index" on url (cost=0.43..816763.00 rows=450 width=1177) (actual time=0.511..28.594 rows=10 loops=1)
Filter: (((domain)::text = 'youtube.com'::text) AND ("timestamp" > (now() - '24:00:00'::interval)))
Rows Removed by Filter: 11106
-> Limit (cost=0.43..859.82 rows=10 width=1177) (actual time=2330.390..5033.214 rows=10 loops=1)
-> Index Scan Backward using "url-likes-index" on url url_1 (cost=0.43..816763.00 rows=9504 width=1177) (actual time=2330.388..5033.200 rows=10 loops=1)
Filter: (((domain)::text = 'twitter.com'::text) AND ("timestamp" > (now() - '24:00:00'::interval)))
Rows Removed by Filter: 1667422
-> Limit (cost=0.43..1446.28 rows=10 width=1177) (actual time=64.748..98.228 rows=10 loops=1)
-> Index Scan Backward using "url-likes-index" on url url_2 (cost=0.43..816763.00 rows=5649 width=1177) (actual time=64.745..98.220 rows=10 loops=1)
Filter: (((domain)::text = 'reddit.com'::text) AND ("timestamp" > (now() - '24:00:00'::interval)))
Rows Removed by Filter: 26739
Planning Time: 3.006 ms
Execution Time: 5162.201 ms
如果您有兴趣自己运行它,go to this link。
我看到一百万条推特行正在被过滤,但我不确定如何避免它。我有一个timestamp
索引,我希望可以使用它而不是按likes
排序并扫描整个内容。这是否意味着我需要一个复合索引?有没有办法让规划器使用两个索引而不是另一个?
附言我想我把主键作为 url 搞砸了。它使索引不必要地变大。
【问题讨论】:
UNION ALL
而不是 UNION
可能会改进一点。
很难判断,因为数字很紧张,但可能会有 5% 的变化。谢谢你提到它。
@a_horse_with_no_name 。 . .虽然是最佳实践,但 union
与 union all
在 30 行上并不会产生很大的不同。
【参考方案1】:
PostgreSQL 尝试使用likes
上的索引来避免排序以获得前 10 个结果,但它必须丢弃许多行才能到达那里。
也许执行计划是最好的,也许不是。
按照以下步骤操作:
在你的桌子上运行ANALYZE
,看看是否能解决问题。
如果没有,请在 (domain, timestamp)
上创建一个索引(按此顺序!),看看这是否会改善问题。
如果这还不够,也可以
删除likes
上的索引(如果可以的话)
或
将ORDER BY likes
更改为ORDER BY likes + 0
。
如果这一切都没有让它变得更好,那么您最初的查询计划是最好的,您所能做的就是使用更多的 RAM,希望更多的数据在缓存中。
【讨论】:
刚刚尝试过ORDER BY likes + 0
,这已经导致 pg 避免使用likes
索引并将我从 20 秒缩短到 0.25。我会测试你的其他想法,但这已经值得了。谢谢!
我建议最后一个,因为它是最不优雅的解决方案。
ANALYZE url
似乎什么也没做。我的索引有点大,所以我有点害羞,但以后可能会尝试。
好吧,那就选择第三个吧。【参考方案2】:
我建议这样编写查询:
SELECT ufiltered.*
FROM (SELECT url.*,
ROW_NUMBER() OVER (PARTITION BY domain ORDER BY likes DESC) AS seqnum
FROM url
WHERE domain IN ('youtube.com', 'twitter.com', 'reddit.com') AND
timestamp > NOW() - INTERVAL '24 hours'
) AS ufiltered
WHERE seqnum <= 10
ORDER BY timestamp DESC
为此,我建议在url(timestamp, domain, likes)
上建立索引。
【讨论】:
我不得不编辑,因为seqnum
没有定义,但是这个查询看起来相当快,没有使用views+0
技巧,而且它短了大约 100 个字符。我会接受的。
当我将它与1 year
一起使用时,我得到了QueryFailedError: temporary file size exceeds temp_file_limit (3193019kB)
。所以我最终使用了问题中的原始查询,没有强制索引。不同的时间范围各自需要不同的索引。以上是关于为啥这个 pg 查询这么慢?我怎样才能让它更快?的主要内容,如果未能解决你的问题,请参考以下文章
在 OpenCl 中,多个 gpu 比单个 gpu 慢。我怎样才能更快?
为啥这个查询这么慢? - PostgreSQL - 从 SERIAL、TIMESTAMP 和 NUMERIC(6,2) 中选择