【发布时间】:2020-08-16 09:39:29
【问题描述】:
我有两张桌子:
products ~ 2000 万条记录
tags ~3500 万条记录
并且对此类查询有性能问题:
SELECT products.name
FROM products
INNER JOIN tags ON tags.product_id=products.id
WHERE
products.categoryid IN (1,2,3..) AND
tags.id = 3
ORDER BY products.position
LIMIT 64 OFFSET 0;
当 OFFSET 很小时,一切都很好。当 OFFSET 大约 >1000 时,查询执行 10-80 秒。
已经有ORDER BY position(产品表)和product_id, id(标签表)的索引。
(OFFSET=100)
Limit (cost=85705.10..147411.97 rows=72 width=667) (actual time=1623.013..2334.542 rows=72 loops=1)
-> Nested Loop (cost=1.13..67736996.34 rows=79036 width=667) (actual time=4.898..2334.493 rows=172 loops=1)
-> Index Scan using items_sorting_index on products (cost=0.56..61905349.96 rows=1513209 width=667) (actual time=0.083..2188.800 rows=17547 loops=1)
Filter: (categoryid = ANY ('{1,2,3,4,5,6,7,8,9,11,12,13,14,15,16,17,18,19,20}'::bigint[]))
Rows Removed by Filter: 638658
-> Index Only Scan using index_tags_on_product_id_and_tag_id on tags (cost=0.56..3.84 rows=1 width=4) (actual time=0.008..0.008 rows=0 loops=17547)
Index Cond: ((product_id = products.id) AND (id = 3))
Heap Fetches: 28
Planning time: 1.345 ms
Execution time: 2334.608 ms
tags.id在查询中可以不同,不同标签的结果元素也不同,所以我觉得不能用cursor-field这样的方法。
如何优化这个查询?
【问题讨论】:
-
您的统计数据看起来有点偏离(产品表中的预期
rows=1513209与实际rows=17547)。您可能想先在两个表上运行ANALYZE。您可以尝试的另一件事是让items_sorting_index索引覆盖name列(请参阅the docs)。 -
@Marth 统计信息已关闭,因为“预期”行没有考虑 LIMIT,而实际行则考虑了。
-
我不明白你对光标的反对意见。还是“光标字段”与光标不同?
-
@jjanes 将字段添加到表中,这将有助于计算页面位置(如allyouneedisbackend.com/blog/2017/09/24/…)。这不是我的问题
-
键集分页应该可以正常工作,只要人们实际上是在逐步浏览数据,而不是想在没有先看到 1-49 的情况下跳到第 50 页。如果 position 不是唯一的,则需要在 ORDER BY 中添加一个 tie breaker。
标签: postgresql performance query-optimization