我正在尝试调试下面的慢查询,但我很难理解它为什么慢。我可以看到计划和子计划都执行索引扫描,包括子计划的“仅索引扫描”,因此两者都应该很快。然而,这个特定查询需要 7 秒。
从这个 EXPLAIN 输出中你知道问题可能出在哪里吗?
select "id", "item_id", "item_name", "type", "updated_time" from "changes"
where (
((type = 1 OR type = 3) AND user_id = 'USER_ID')
or type = 2 AND item_id IN (SELECT item_id FROM user_items WHERE user_id = 'USER_ID')
) and "counter" > '35885954' order by "counter" asc limit 100;
Limit (cost=8409.70..8553.44 rows=100 width=101) (actual time=7514.730..7514.731 rows=0 loops=1)
-> Index Scan using changes_pkey on changes (cost=8409.70..2387708.44 rows=1655325 width=101) (actual time=7514.728..7514.729 rows=0 loops=1)
Index Cond: (counter > 35885954)
Filter: ((((type = 1) OR (type = 3)) AND ((user_id)::text = 'USER_ID'::text)) OR ((type = 2) AND (hashed SubPlan 1)))
Rows Removed by Filter: 11378536
SubPlan 1
-> Index Only Scan using user_items_user_id_item_id_unique on user_items (cost=0.56..8401.57 rows=3030 width=24) (actual time=0.085..3.011 rows=3589 loops=1)
Index Cond: (user_id = 'USER_ID'::text)
Heap Fetches: 2053
Planning Time: 0.245 ms
Execution Time: 7514.781 ms
(11 rows)
仅仅因为使用索引来读取 11,378,536 行并不会神奇地快。这就是问题所在,它必须读取该行数。
我们不知道复杂的过滤条件的哪些部分导致大多数行失败,这使得很难自信地提出优化。
(type, counter)
也许对或进行索引(type, user_id)
会有所帮助。或者,您可以根据 OR 条件将查询分成两部分,然后使用 UNION 将它们组合起来。沿着丑陋的方向分割查询
OR
。生成的两个查询是互斥的,因此将它们与 组合起来UNION ALL
:使用部分多列索引支持第一个,
SELECT
使其快如闪电:看:
第二个
SELECT
不太清楚。领先的索引counter
应该在LIMIT
较小并且item_id
给定的份额user_id
不太小时时效果最好。否则,切换索引列
(item_id, counter)
可能会更好。LIMIT 100
我为第一个单独插入了一个SELECT
,这在逻辑上是多余的,但可能会产生更好的查询计划。第二个LIMIT 100
适用于整个查询。它甚至可能有助于将一个单独的附加LIMIT 100
到第二个SELECT
(带有一组额外的括号)。没有把握。测试并报告哪个效果最好。