【问题标题】:Grouping is very slow on PostgreSQL even with index即使使用索引,PostgreSQL 上的分组也非常慢
【发布时间】:2021-11-10 16:39:39
【问题描述】:

我有一个包含 94M 记录的日志表,并托管在 16GB 的 RAM、6CPU 和快速 SSD 上。

Log 表有多个字段,但查询使用三个主要字段运行,UserId(int)、DateStamp(timestamp) 和 Result(boolean)。

这些字段也有一个索引

create index "IX_Log_UserId_DateStampDate_Result"
    on "Log" ("""UserId""", ("DateStamp"::date), """Result""");

我们将 DateStamp 转换为索引中的 Date,因为我们将 DateStamp 分组为 Date。

SQL

SELECT "Log"."UserId",
       date("Log"."DateStamp") AS "Date",
       count(*)                AS "Conversions",
       "Log"."Result"
FROM "Log"
GROUP BY "Log"."UserId", (date("Log"."DateStamp")), "Log"."Result"

执行计划(看起来好像没有在分组中使用索引。)

Group  (cost=16701795.29..17867897.49 rows=93288176 width=9)
"  Group Key: ""UserId"", (date(""DateStamp"")), ""Result"""
  ->  Sort  (cost=16701795.29..16935015.73 rows=93288176 width=9)
"        Sort Key: ""UserId"", (date(""DateStamp"")), ""Result"""
"        ->  Seq Scan on ""Log""  (cost=0.00..3100034.20 rows=93288176 width=9)"

问题是执行分组查询大约需要 4 分钟,在迁移到 PostgreSQL 之前,我们一直在 MS SQL 上运行相同的数据库,而 MS SQL 在同一台 PC 上执行该查询只需 10 秒。

其他 PostgreSQL 参数

work_mem=32000kb (changing this do not makes any difference)
shared_buffers=200MB
effective_cache_size=12GB

已编辑

我还添加了一个扩展的解释计划。

GroupAggregate  (cost=16701795.29..19033999.69 rows=93288176 width=17) (actual time=278258.047..516820.577 rows=331187 loops=1)
  Group Key: ""UserId"", (date(""DateStamp"")), ""Result""
  Buffers: shared hit=31437 read=1902506, temp read=216860 written=217003
  I/O Timings: read=9319.798
  ->  Sort  (cost=16701795.29..16935015.73 rows=93288176 width=9) (actual time=278258.028..403513.564 rows=93288174 loops=1)
        Sort Key: ""UserId"", (date(""DateStamp"")), ""Result"""
        Sort Method: external merge  Disk: 1734880kB
        Buffers: shared hit=31437 read=1902506, temp read=216860 written=217003
        I/O Timings: read=9319.798
        ->  Seq Scan on ""Log""  (cost=0.00..3100034.20 rows=93288176 width=9) (actual time=0.021..137339.749 rows=93288174 loops=1)
              Buffers: shared hit=31426 read=1902506
              I/O Timings: read=9319.798
Planning Time: 0.700 ms
Execution Time: 517321.399 ms

【问题讨论】:

  • 没关系,但有什么理由不放弃GROUP BY 子句而改为使用SELECT DISTINCT ...?
  • @Tim Biegeleisen 我不确定你的意思,但是分组和区分是两个不同的东西。我需要按日期、用户 ID 和结果(真或假)分组。
  • ...在这种情况下它们是相同的
  • @a_horse_with_no_name 但在此查询中,只需要一个 SORT,因为这本质上是一个 DISTINCT。我们需要查看执行计划并从 SQL Server 中“借用”一些技巧
  • 你真的应该摆脱那些可怕的引用标识符。在名称中包含双引号 (""UserId"") 更糟糕

标签: sql postgresql


【解决方案1】:

我检查了你的解释分析结果。但是您的数据库使用全表扫描而不是索引扫描,可能您的索引不正确或者您的索引与您的分组字段不匹配。

请查看我的代码:

create index "IX_person_mobile_main_mobile_id_date"
    on person_mobile_main (mobile_id, (insert_date::date)); 

explain (analyze, buffers, format text)   
select mobile_id, insert_date::date, count(*) from examples.person_mobile_main
group by mobile_id, insert_date::date;

结果:

GroupAggregate  (cost=0.42..70002.48 rows=547265 width=16) (actual time=0.165..683.103 rows=547265 loops=1)
  Group Key: mobile_id, (insert_date)::date
  Buffers: shared hit=513538 read=1498
  ->  Index Scan using "IX_person_mobile_main_mobile_id_date" on person_mobile_main  (cost=0.42..59057.18 rows=547265 width=8) (actual time=0.141..424.740 rows=547265 loops=1)
        Buffers: shared hit=513538 read=1498
Planning Time: 0.250 ms
Execution Time: 718.435 ms

在我的结果中显示Index Scan using "IX_person_mobile_main_mobile_id_date" 但是在您显示的结果中Seq Scan on "Log"。您没有 Index Scan 关键字。

【讨论】:

  • 可能是因为表有数百万条记录,查询规划器决定不使用索引。
  • 查询规划器不可能不对数百万条记录使用索引。相反,数百万、数十亿条记录都需要索引
  • @Tomas 我可以重复他的发现,即使有数百万行。
  • @RaminFaracov 这简直是不可能的thoughtbot.com/blog/why-postgres-wont-always-use-an-index 你也可以在谷歌google.com/…找到几十篇关于它的文章
猜你喜欢
  • 1970-01-01
  • 2012-07-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-30
  • 2018-02-02
  • 1970-01-01
  • 2013-04-20
相关资源
最近更新 更多