【问题标题】:PostgreSQL not using index for very big tablePostgreSQL 不为非常大的表使用索引
【发布时间】:2021-11-30 00:07:34
【问题描述】:

raw_data 有一个索引ix_raw_data_timestamp

CREATE TABLE IF NOT EXISTS public.raw_data
(
    ts timestamp without time zone NOT NULL,
    log_msg character varying COLLATE pg_catalog."default",
    log_image bytea
)
CREATE INDEX IF NOT EXISTS ix_raw_data_timestamp
    ON public.raw_data USING btree
    (ts ASC NULLS LAST)
    TABLESPACE pg_default;

由于某种原因,索引没有用于以下查询(因此非常慢):

SELECT ts,
    log_msg
FROM raw_data
ORDER BY ts ASC
LIMIT 5e6;

EXPLAIN (analyze, buffers, format text) 上面查询的结果:

                                                                       QUERY PLAN                                                                       
--------------------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=9752787.07..10336161.14 rows=5000000 width=50) (actual time=789124.600..859046.614 rows=5000000 loops=1)
   Buffers: shared hit=12234 read=888521, temp read=2039471 written=2664654
   ->  Gather Merge  (cost=9752787.07..18421031.89 rows=74294054 width=50) (actual time=789085.442..822547.099 rows=5000000 loops=1)
         Workers Planned: 2
         Workers Launched: 2
         Buffers: shared hit=12234 read=888521, temp read=2039471 written=2664654
         ->  Sort  (cost=9751787.05..9844654.62 rows=37147027 width=50) (actual time=788203.880..795491.054 rows=1667070 loops=3)
               Sort Key: "ts"
               Sort Method: external merge  Disk: 1758904kB
               Worker 0:  Sort Method: external merge  Disk: 1762872kB
               Worker 1:  Sort Method: external merge  Disk: 1756216kB
               Buffers: shared hit=12234 read=888521, temp read=2039471 written=2664654
               ->  Parallel Seq Scan on raw_data  (cost=0.00..1272131.27 rows=37147027 width=50) (actual time=25.436..119352.861 rows=29717641 loops=3)
                     Buffers: shared hit=12141 read=888520
 Planning Time: 5.240 ms
 JIT:
   Functions: 7
   Options: Inlining true, Optimization true, Expressions true, Deforming true
   Timing: Generation 0.578 ms, Inlining 76.678 ms, Optimization 24.578 ms, Emission 13.060 ms, Total 114.894 ms
 Execution Time: 877489.531 ms
(20 rows)

但它是用于这个的:

SELECT ts,
    log_msg
FROM raw_data
ORDER BY ts ASC
LIMIT 4e6;

上面查询的EXPLAIN (analyze, buffers, format text)是:

                                                                           QUERY PLAN                                                                           
----------------------------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=0.57..9408157.15 rows=4000000 width=50) (actual time=15.081..44747.127 rows=4000000 loops=1)
   Buffers: shared hit=24775 read=61155
   ->  Index Scan using ix_raw_data_timestamp on raw_data  (cost=0.57..209691026.73 rows=89152864 width=50) (actual time=2.218..16077.755 rows=4000000 loops=1)
         Buffers: shared hit=24775 read=61155
 Planning Time: 1.306 ms
 JIT:
   Functions: 3
   Options: Inlining true, Optimization true, Expressions true, Deforming true
   Timing: Generation 0.406 ms, Inlining 1.121 ms, Optimization 7.917 ms, Emission 3.721 ms, Total 13.165 ms
 Execution Time: 59028.951 ms
(10 rows)

不用说,目的是让所有查询都使用索引,无论大小如何,但我似乎找不到解决方案。

PS:

  • 数据库中有大约89152922 行。

编辑:

将内存增加到 2G (SET work_mem = '2GB';) 后,查询速度稍微快了一点(不再使用磁盘)但仍然没有那么快:

                                                                       QUERY PLAN                                                                       
--------------------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=5592250.54..6175624.61 rows=5000000 width=50) (actual time=215887.445..282393.743 rows=5000000 loops=1)
   Buffers: shared hit=12224 read=888531
   ->  Gather Merge  (cost=5592250.54..14260731.75 rows=74296080 width=50) (actual time=215874.072..247030.062 rows=5000000 loops=1)
         Workers Planned: 2
         Workers Launched: 2
         Buffers: shared hit=12224 read=888531
         ->  Sort  (cost=5591250.52..5684120.62 rows=37148040 width=50) (actual time=215854.323..221828.921 rows=1667147 loops=3)
               Sort Key: "ts"
               Sort Method: top-N heapsort  Memory: 924472kB
               Worker 0:  Sort Method: top-N heapsort  Memory: 924379kB
               Worker 1:  Sort Method: top-N heapsort  Memory: 924281kB
               Buffers: shared hit=12224 read=888531
               ->  Parallel Seq Scan on raw_data  (cost=0.00..1272141.40 rows=37148040 width=50) (actual time=25.899..107034.903 rows=29717641 loops=3)
                     Buffers: shared hit=12130 read=888531
 Planning Time: 0.058 ms
 JIT:
   Functions: 7
   Options: Inlining true, Optimization true, Expressions true, Deforming true
   Timing: Generation 0.642 ms, Inlining 53.860 ms, Optimization 23.848 ms, Emission 11.768 ms, Total 90.119 ms
 Execution Time: 300281.654 ms
(20 rows)

【问题讨论】:

  • edit您的问题并添加使用explain (analyze, buffers, format text)生成的execution plan不是只是一个“简单”解释)为formatted text,并确保保留计划的缩进。粘贴文本,然后将``` 放在计划前一行和计划后一行。
  • @jjanes - 我正在尝试让它使用索引,以便更快,4e6 查询就是这种情况。
  • 你可以通过set enable_sort=off强制它使用索引。但这不是一个好的永久解决方案,所以显示 `explain (analyze, buffers, format text)` 查询运行双向,所以我们可以建议如何让它自己做出更好的决定。如果 track_io_timing 尚未打开,请先打开它。
  • 你试过includinglog_msg作为非键索引列吗?如果消息足够小/短,这可能会有所帮助,但也会减慢速度。
  • 为什么你认为使用索引总是(!)更快?没有解释的结果,只有假设。

标签: postgresql


【解决方案1】:

这里的问题是您要进行 PARALLEL SEQ SCAN 和 GATHER_MERGE。收集合并需要 74,294,054 行以输出 5,000,000。这是有道理的,因为您说数据库中有 89,152,922 行,而您没有条件限制它们。

它为什么会选择这个计划,可能是因为它正在强制实现,因为你已经结束了work_mem。所以增加你的work_mem。如果 PostgreSQL 认为它可以将所有这些都放在内存中并且不必在磁盘上执行此操作,那么它将大大加快速度。

【讨论】:

  • 那么work_mem 是否需要设置为大于external merge Disk: 1758904kB 才能获得最佳结果?
  • 它可能必须设置为更多。我不确定这个数字是查询上的交换空间(即,在它用完 work_mem 之后)还是包括 work_mem 在内的总空间。但是你可以将你的 work_mem 调高到一个淫秽的数字,以确定是否可以修复它,然后从那里开始使用它。
  • 我已更新问题以包含 work_mem 的新设置。但是,它仍然在进行 Gather->Sort,而不是 Index Scan。还有其他设置吗?
猜你喜欢
  • 1970-01-01
  • 2016-06-05
  • 2015-02-06
  • 1970-01-01
  • 2019-04-29
  • 1970-01-01
  • 2018-08-22
  • 1970-01-01
相关资源
最近更新 更多