【问题标题】:Why does postgres do a table scan instead of using my index?为什么 postgres 进行表扫描而不是使用我的索引?
【发布时间】:2018-11-08 02:00:00
【问题描述】:

我正在使用 Postgres 中的 HackerNews 数据集。大约有 1700 万行,其中大约 1450 万是 cmets,大约 250 万是故事。有一个名为“rbanffy”的非常活跃的用户,他有 25k 次提交,大约相等的拆分故事/cmets。 “by”和“type”都有单独的索引。

我有一个问题:

SELECT *
FROM "hn_items"
WHERE by = 'rbanffy'
and type = 'story'
ORDER BY id DESC
LIMIT 20 OFFSET 0

运行速度很快(它使用“by”索引)。如果我将类型更改为“评论”,那么它会非常慢。从解释来看,它不使用任何一个索引并进行扫描。

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”索引并快速执行。

为什么会这样?我从https://stackoverflow.com/a/309814/214545 了解到,必须进行这样的破解意味着有问题。但我不知道是什么。

编辑:
这是 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,则使用完整扫描。我认为这个数字并不神奇,只是恰好是我的数据集或我不知道的其他变量的阈值。我不知道这是否有帮助。

【问题讨论】:

  • @a_horse_with_no_name - 我不知道如何显示更多的执行计划 - 我运行了“EXPLAIN ”,这就是结果。我还应该做什么?运行分析没有改变任何东西。
  • 这可能是数据的物理排序问题。我们可以输出EXPLAIN (ANALYZE, BUFFERS) 吗?
  • @LaurenzAlbe - 已添加。请注意,PK id 是索引 ASC,我正在订购 DESC。但是我尝试将我的顺序更改为 ASC,但它并没有改变解释。 (我在“索引扫描”之后删除了“向后”以避免混淆 - 因为它似乎没有任何区别。)
  • 记录在案 - 如果我删除“order by”,它将使用正确的索引而无需破解。如果我将 order by 更改为“by”或“type”而不是“id”,它会使用索引(但这不是我想要的..)当我选择“order by id”(asc||desc)时 - 它不会不要使用它。这就是使用 type="comment" 时的全部内容 - type="story" 始终有效。
  • 优化器似乎低估了查询返回的行数,并认为对行进行排序的开销大于使用“错误”索引的开销。请编辑问题并添加索引hn_items_by_index 的定义。您能否为 fast 查询显示explain (analyze, buffers) 的输出?

标签: postgresql indexing sql-execution-plan postgresql-performance


【解决方案1】:

由于rbanffy 吃了很多comments,PostgreSQL 假设如果它按照ORDER BY 子句(可以使用主键索引)暗示的顺序搜索表,它将足够快,直到它已找到符合搜索条件的 20 行。

不幸的是,这家伙最近变得懒惰了——无论如何,PostgreSQL 必须扫描 46798 最高的ids,直到找到它的 20 个命中。 (你真的不应该删除Backwards,这让我很困惑。)

解决这个问题的最佳方法是混淆 PostgreSQL,使其不选择主键索引,可能像这样:

SELECT *
FROM (SELECT * FROM hn_items
      WHERE by = 'rbanffy'
        AND type = 'comment'
      OFFSET 0) q
ORDER BY id DESC
LIMIT 20;

【讨论】:

  • 我想知道在两列上创建扩展统计信息是否会对优化器有所帮助
  • 我对此表示怀疑,因为这里的问题不是估计错误,而是值的不幸分布。
  • 感谢您的回答 - 消化。关于Backwards,我可以使用order by id ASC 给出相同的查询,它不会打印Backwards,也不会让它变得更好。所以我不确定为什么它应该有所作为
  • 它可能没有什么区别,但是对输出的任何修改都会令人困惑。如果没有“向后”,那就意味着主键索引是按降序排列的,这是不可能的。
  • 顺便说一句,我认为连接黑客的含义只是防止特定索引-通过添加动态表达式(在您的情况下-防止索引“类型”)postgresql.org/docs/9.6/static/indexes-expressional.html`
猜你喜欢
  • 2023-04-04
  • 1970-01-01
  • 1970-01-01
  • 2023-03-29
  • 2020-02-14
  • 1970-01-01
  • 2022-01-15
  • 2011-09-25
  • 1970-01-01
相关资源
最近更新 更多