为什么 postgres 做 table 扫描而不是使用我的索引?
Why does postgres do a table scan instead of using my index?
我正在使用 Postgres 中的 HackerNews 数据集。大约有 1700 万行,其中约 1450 万行是评论,约 250 万行是故事。有一个名为 "rbanffy" 的非常活跃的用户,他有 25k 次提交,大约平均分配 stories/comments。 "by" 和 "type" 都有单独的索引。
我有一个问题:
SELECT *
FROM "hn_items"
WHERE by = 'rbanffy'
and type = 'story'
ORDER BY id DESC
LIMIT 20 OFFSET 0
运行速度很快(它使用 'by' 索引)。如果我将类型更改为 "comment" 那么它会非常慢。从解释来看,它不使用任何索引并进行扫描。
Limit (cost=0.56..56948.32 rows=20 width=1937)
-> Index Scan using hn_items_pkey on hn_items (cost=0.56..45823012.32 rows=16093 width=1937)
Filter: (((by)::text = 'rbanffy'::text) AND ((type)::text = 'comment'::text))
如果我将查询更改为 type||''='comment'
,那么它将使用 'by' 索引并快速执行。
为什么会这样?我从 了解到,必须像这样进行黑客攻击意味着有问题。但是我不知道是什么。
编辑:
这是 type='story'
的解释
Limit (cost=72553.07..72553.12 rows=20 width=1255)
-> Sort (cost=72553.07..72561.25 rows=3271 width=1255)
Sort Key: id DESC
-> Bitmap Heap Scan on hn_items (cost=814.59..72466.03 rows=3271 width=1255)
Recheck Cond: ((by)::text = 'rbanffy'::text)
Filter: ((type)::text = 'story'::text)
-> Bitmap Index Scan on hn_items_by_index (cost=0.00..813.77 rows=19361 width=0)
Index Cond: ((by)::text = 'rbanffy'::text)
编辑:
解释(分析,缓冲区)
Limit (cost=0.56..59510.10 rows=20 width=1255) (actual time=20.856..545.282 rows=20 loops=1)
Buffers: shared hit=21597 read=2658 dirtied=32
-> Index Scan using hn_items_pkey on hn_items (cost=0.56..47780210.70 rows=16058 width=1255) (actual time=20.855..545.271 rows=20 loops=1)
Filter: (((by)::text = 'rbanffy'::text) AND ((type)::text = 'comment'::text))
Rows Removed by Filter: 46798
Buffers: shared hit=21597 read=2658 dirtied=32
Planning time: 0.173 ms
Execution time: 545.318 ms
编辑:type='story'
的解释(分析,缓冲区)
Limit (cost=72553.07..72553.12 rows=20 width=1255) (actual time=44.121..44.127 rows=20 loops=1)
Buffers: shared hit=20137
-> Sort (cost=72553.07..72561.25 rows=3271 width=1255) (actual time=44.120..44.123 rows=20 loops=1)
Sort Key: id DESC
Sort Method: top-N heapsort Memory: 42kB
Buffers: shared hit=20137
-> Bitmap Heap Scan on hn_items (cost=814.59..72466.03 rows=3271 width=1255) (actual time=6.778..37.774 rows=11630 loops=1)
Recheck Cond: ((by)::text = 'rbanffy'::text)
Filter: ((type)::text = 'story'::text)
Rows Removed by Filter: 12587
Heap Blocks: exact=19985
Buffers: shared hit=20137
-> Bitmap Index Scan on hn_items_by_index (cost=0.00..813.77 rows=19361 width=0) (actual time=3.812..3.812 rows=24387 loops=1)
Index Cond: ((by)::text = 'rbanffy'::text)
Buffers: shared hit=152
Planning time: 0.156 ms
Execution time: 44.422 ms
编辑:最新测试结果
我正在研究 type='comment'
查询并注意到如果将限制更改为更高的数字(例如 100),它会使用 by
索引。我一直在研究这些值,直到发现关键数字是“47”。如果限制为 47,则使用 by
索引,如果限制为 46,则为全扫描。我认为这个数字并不神奇,只是恰好是我的数据集或其他一些我不知道的变量的阈值。不知道有没有帮助。
因为 rbanffy
吃了很多 comment
,PostgreSQL 假定如果它按照 ORDER BY
隐含的顺序搜索 table 就足够快了子句(可以使用主键索引),直到找到符合搜索条件的20行。
不幸的是,这家伙最近变得懒惰了——无论如何,PostgreSQL 必须扫描 46798 个最高的 id
s,直到它找到它的 20 个命中。 (你真的不应该删除让我感到困惑的 Backwards
。)
最好的解决方法是混淆 PostgreSQL,使其不选择主键索引,也许像这样:
SELECT *
FROM (SELECT * FROM hn_items
WHERE by = 'rbanffy'
AND type = 'comment'
OFFSET 0) q
ORDER BY id DESC
LIMIT 20;
我正在使用 Postgres 中的 HackerNews 数据集。大约有 1700 万行,其中约 1450 万行是评论,约 250 万行是故事。有一个名为 "rbanffy" 的非常活跃的用户,他有 25k 次提交,大约平均分配 stories/comments。 "by" 和 "type" 都有单独的索引。
我有一个问题:
SELECT *
FROM "hn_items"
WHERE by = 'rbanffy'
and type = 'story'
ORDER BY id DESC
LIMIT 20 OFFSET 0
运行速度很快(它使用 'by' 索引)。如果我将类型更改为 "comment" 那么它会非常慢。从解释来看,它不使用任何索引并进行扫描。
Limit (cost=0.56..56948.32 rows=20 width=1937)
-> Index Scan using hn_items_pkey on hn_items (cost=0.56..45823012.32 rows=16093 width=1937)
Filter: (((by)::text = 'rbanffy'::text) AND ((type)::text = 'comment'::text))
如果我将查询更改为 type||''='comment'
,那么它将使用 'by' 索引并快速执行。
为什么会这样?我从 了解到,必须像这样进行黑客攻击意味着有问题。但是我不知道是什么。
编辑:
这是 type='story'
Limit (cost=72553.07..72553.12 rows=20 width=1255)
-> Sort (cost=72553.07..72561.25 rows=3271 width=1255)
Sort Key: id DESC
-> Bitmap Heap Scan on hn_items (cost=814.59..72466.03 rows=3271 width=1255)
Recheck Cond: ((by)::text = 'rbanffy'::text)
Filter: ((type)::text = 'story'::text)
-> Bitmap Index Scan on hn_items_by_index (cost=0.00..813.77 rows=19361 width=0)
Index Cond: ((by)::text = 'rbanffy'::text)
编辑: 解释(分析,缓冲区)
Limit (cost=0.56..59510.10 rows=20 width=1255) (actual time=20.856..545.282 rows=20 loops=1)
Buffers: shared hit=21597 read=2658 dirtied=32
-> Index Scan using hn_items_pkey on hn_items (cost=0.56..47780210.70 rows=16058 width=1255) (actual time=20.855..545.271 rows=20 loops=1)
Filter: (((by)::text = 'rbanffy'::text) AND ((type)::text = 'comment'::text))
Rows Removed by Filter: 46798
Buffers: shared hit=21597 read=2658 dirtied=32
Planning time: 0.173 ms
Execution time: 545.318 ms
编辑:type='story'
Limit (cost=72553.07..72553.12 rows=20 width=1255) (actual time=44.121..44.127 rows=20 loops=1)
Buffers: shared hit=20137
-> Sort (cost=72553.07..72561.25 rows=3271 width=1255) (actual time=44.120..44.123 rows=20 loops=1)
Sort Key: id DESC
Sort Method: top-N heapsort Memory: 42kB
Buffers: shared hit=20137
-> Bitmap Heap Scan on hn_items (cost=814.59..72466.03 rows=3271 width=1255) (actual time=6.778..37.774 rows=11630 loops=1)
Recheck Cond: ((by)::text = 'rbanffy'::text)
Filter: ((type)::text = 'story'::text)
Rows Removed by Filter: 12587
Heap Blocks: exact=19985
Buffers: shared hit=20137
-> Bitmap Index Scan on hn_items_by_index (cost=0.00..813.77 rows=19361 width=0) (actual time=3.812..3.812 rows=24387 loops=1)
Index Cond: ((by)::text = 'rbanffy'::text)
Buffers: shared hit=152
Planning time: 0.156 ms
Execution time: 44.422 ms
编辑:最新测试结果
我正在研究 type='comment'
查询并注意到如果将限制更改为更高的数字(例如 100),它会使用 by
索引。我一直在研究这些值,直到发现关键数字是“47”。如果限制为 47,则使用 by
索引,如果限制为 46,则为全扫描。我认为这个数字并不神奇,只是恰好是我的数据集或其他一些我不知道的变量的阈值。不知道有没有帮助。
因为 rbanffy
吃了很多 comment
,PostgreSQL 假定如果它按照 ORDER BY
隐含的顺序搜索 table 就足够快了子句(可以使用主键索引),直到找到符合搜索条件的20行。
不幸的是,这家伙最近变得懒惰了——无论如何,PostgreSQL 必须扫描 46798 个最高的 id
s,直到它找到它的 20 个命中。 (你真的不应该删除让我感到困惑的 Backwards
。)
最好的解决方法是混淆 PostgreSQL,使其不选择主键索引,也许像这样:
SELECT *
FROM (SELECT * FROM hn_items
WHERE by = 'rbanffy'
AND type = 'comment'
OFFSET 0) q
ORDER BY id DESC
LIMIT 20;