【问题标题】:Postgres uses Bitmap Index Scan instead of normal Index ScanPostgres 使用位图索引扫描而不是普通的索引扫描
【发布时间】:2020-02-14 22:31:15
【问题描述】:

我有两张 Postgres 表:一张普通表(20M 行)和一张物化视图(2M 行)。两个表的“时间”列都有索引。在这两个表上,我正在运行一个在日期范围内聚合的查询。当我在普通表上运行查询时,Postgres 使用索引扫描,大约需要 1 秒。但是,在物化视图上,Postgres 使用 Bitmap Index Scan,大约需要 7 秒。我不确定为什么这里没有使用正常的索引扫描。

两张表大致相似;物化视图有 3 个额外的列(2 个浮点数和一个布尔值)。这两个表的id 列和time 列都有索引;在普通表中,id 列是主键。这两个表都是VACUUM ANALYZEd。

查询如下:

SELECT count(*) as post_count,
    sum(CASE WHEN in_reply_to_post_id IS NULL THEN 0 ELSE 1 END) as replies,
    date_trunc('hour', time) as time_interval
FROM posts_enriched_materialized_2_weeks
WHERE time >= '2019-10-10' AND time < '2019-10-11'
GROUP BY time_interval;

EXPLAIN ANALYZE在物化视图上的结果如下:

GroupAggregate  (cost=264262.43..268387.60 rows=158191 width=32) (actual time=7794.743..7893.961 rows=24 loops=1)
  Group Key: (date_trunc('hour'::text, "time"))
  ->  Sort  (cost=264262.43..264691.99 rows=171822 width=46) (actual time=7790.080..7838.184 rows=175691 loops=1)
        Sort Key: (date_trunc('hour'::text, "time"))
        Sort Method: external merge  Disk: 5464kB
        ->  Bitmap Heap Scan on posts_enriched_materialized_2_weeks  (cost=4057.61..244033.53 rows=171822 width=46) (actual time=21.184..7672.166 rows=175691 loops=1)
              Recheck Cond: (("time" >= '2019-10-10 00:00:00'::timestamp without time zone) AND ("time" < '2019-10-11 00:00:00'::timestamp without time zone))
              Heap Blocks: exact=17420
              ->  Bitmap Index Scan on posts_enriched_materialized_2_weeks_time_index  (cost=0.00..4014.65 rows=171822 width=0) (actual time=18.551..18.551 rows=175691 loops=1)
                    Index Cond: (("time" >= '2019-10-10 00:00:00'::timestamp without time zone) AND ("time" < '2019-10-11 00:00:00'::timestamp without time zone))
Planning time: 0.106 ms
Execution time: 7894.874 ms

编辑:非MV表上EXPLAIN ANALYZE的结果如下:

GroupAggregate  (cost=193490.77..197635.89 rows=150641 width=32) (actual time=1168.018..1267.225 rows=24 loops=1)
  Group Key: (date_trunc('hour'::text, "time"))
  ->  Sort  (cost=193490.77..193943.19 rows=180969 width=46) (actual time=1163.293..1210.887 rows=175701 loops=1)
        Sort Key: (date_trunc('hour'::text, "time"))
        Sort Method: external merge  Disk: 5472kB
        ->  Index Scan using posts_time_index on tweets  (cost=0.44..172118.80 rows=180969 width=46) (actual time=0.900..1065.469 rows=175701 loops=1)
              Index Cond: (("time" >= '2019-10-10 00:00:00'::timestamp without time zone) AND ("time" < '2019-10-11 00:00:00'::timestamp without time zone))
Planning time: 0.514 ms
Execution time: 1268.219 ms

下面是 cmets 中请求的每个表的 time 列的相关性。我不完全知道这个值指的是什么,但它看起来非常相关:

posts   0.8844374
posts_enriched_materialized_2_weeks 0.09846322

【问题讨论】:

  • Bitmap Index/Heap Scan 并不是减慢 MV 查询速度的原因。相反,花费大部分时间的是external merge Disk: 5464kB。你的work_mem 设置是什么?也许您可以将其增加到至少 6MB?
  • 真的吗? actual time 的排序是 7838 毫秒,Bitmap Index Scan 只需要 18 毫秒
  • 请在另一张桌子上出示解释计划。
  • @richyen 实际上排序占用的时间很少。需要时间的是位图堆扫描(远远超过 90%)。为排序给出的时间包括排序“之前”步骤的时间,因此排序本身只需要大约 7838 - 7672 = 166 毫秒。
  • 旁白:将sum(CASE WHEN in_reply_to_post_id IS NULL THEN 0 ELSE 1 END) 替换为等效的count(in_reply_to_post_id)。更简单,可能更快。

标签: postgresql


【解决方案1】:

在低相关性的情况下,planner 认为如果进行普通的索引扫描,它会在整个表(物化视图)中跳跃以获取指示的行,从而导致大量随机 IO,这比顺序慢得多IO。通过进行位图扫描,它可以改善这一点,因为位图固有地将索引中的行“排序”成它在表中找到它们的顺序,从而使表读取更具顺序性。

相关性高的情况下,在进行常规索引扫描时,自然会或多或少地按顺序读取表,因为表和索引的顺序大多是相同的。此外,它将读取表格的一小部分。如果相关性是完美的,并且您读取了 1/100 的索引,那么您只会读取构成表的大约 1/100 的页面,并且会按顺序读取。因为您已经获得顺序读取,所以切换到位图扫描不会给您带来任何好处,但它确实有成本。

在您的情况下,这种改进似乎效果不佳。可能是您需要的数据在物化视图中足够稀疏,从 IO 系统的角度来看,按顺序读取数据看起来仍然比按顺序读取更随机。

另一个问题可能是您的表被频繁使用,因此在缓存中“热”,而您的物化视图很少使用,因此“冷”。这并不能解释为什么它选择了位图扫描,但可以解释为什么位图扫描不是很有效。

您可以在物化视图的定义中添加“按时间排序”,以按时间列对其进行聚类。

如果这不能解决问题,那么增加“effective_io_concurrency”可能会有所帮助,特别是如果您有 RAID 或 JBOD。通过让位图堆扫描尝试从所有不同的主轴预取页面,它可以增加您的 IO 吞吐量。

【讨论】:

  • 感谢您的回答!在物化视图的定义中添加ORDER BY time DESC 一开始效果非常好。然而,在周末多次刷新物化视图后,time 上的相关性似乎回落到几乎为 0,并且物化视图已经回到使用位图索引扫描。你知道为什么会这样吗?谢谢!
  • 附加更新:我尝试非并发刷新物化视图(与我通常使用的并发相反),相关性变为-1。我想这与刷新如何与磁盘上的行顺序交互有关。你知道我如何能够通过并发刷新来获得这种行为吗?谢谢!
  • 我不认为“并发”可以严格遵守“顺序”,但这确实让我感到惊讶。当它回到位图扫描时​​,它是否也回到了旧的慢速?同时刷新时,桌面上还有多少其他活动?
  • 是的,位图扫描的速度和以前一样。此外,当同时刷新时,桌子上没有任何活动。
  • 能否给出物化视图的定义?或者至少描述一下,聚合、连接等。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-29
  • 2021-08-12
  • 2021-03-19
  • 1970-01-01
  • 2022-01-15
  • 1970-01-01
相关资源
最近更新 更多