【问题标题】:Large COUNT DISTINCT performs slowly in postgresql大 COUNT DISTINCT 在 postgresql 中执行缓慢
【发布时间】:2020-10-04 09:39:26
【问题描述】:

我正在对 postgresql 12 上的表运行一个大计数(DISTINCT)查询组。该表大约为 32GB,300MM 行。它按年份划分。这些组或多或少完全分布:

EXPLAIN (ANALYZE,BUFFERS) 
SELECT 
date_trunc('month', condition_start_date::timestamp) as dt, 
condition_source_value, 
COUNT(DISTINCT person_id) 
FROM synpuf5.condition_occurrence_yrpart 
GROUP BY date_trunc('month', condition_start_date::timestamp), condition_source_value 
ORDER BY COUNT(DISTINCT person_id) DESC LIMIT 10;

这是查询计划器的输出:

QUERY PLAN
Limit  (cost=50052765961.82..50052765961.85 rows=10 width=21) (actual time=691022.306..691022.308 rows=10 loops=1)
   Buffers: shared hit=3062256 read=222453
   ->  Sort  (cost=50052765961.82..50052777188.87 rows=4490820 width=21) (actual time=690786.364..690786.364 rows=10 loops=1)
         Sort Key: (count(DISTINCT condition_occurrence_yrpart_2007.person_id)) DESC
         Sort Method: top-N heapsort  Memory: 26kB
         Buffers: shared hit=3062256 read=222453
         ->  GroupAggregate  (cost=50049709699.80..50052668916.82 rows=4490820 width=21) (actual time=567099.326..690705.612 rows=360849 loops=1)
               Group Key: (date_trunc('month'::text, (condition_occurrence_yrpart_2007.condition_start_date)::timestamp without time zone)), condition_occurrence_yrpart_2007.condition_source_value
               Buffers: shared hit=3062253 read=222453
               ->  Sort  (cost=50049709699.80..50050432663.48 rows=289185472 width=17) (actual time=567098.345..619461.044 rows=289182385 loops=1)
                     Sort Key: (date_trunc('month'::text, (condition_occurrence_yrpart_2007.condition_start_date)::timestamp without time zone)), condition_occurrence_yrpart_2007.condition_source_value
                     Sort Method: quicksort  Memory: 30333184kB
                     Buffers: shared hit=3062246 read=222453
                     ->  Append  (cost=10000000000.00..50009068412.44 rows=289185472 width=17) (actual time=0.065..74222.771 rows=289182385 loops=1)
                           Buffers: shared hit=3062240 read=222453
                           ->  Seq Scan on condition_occurrence_yrpart_2007  (cost=10000000000.00..10000001125.61 rows=42774 width=17) (actual time=0.064..13.756 rows=42774 loops=1)
                                 Buffers: shared read=484
                           ->  Seq Scan on condition_occurrence_yrpart_2008  (cost=10000000000.00..10002732063.72 rows=103678448 width=17) (actual time=0.039..21209.532 rows=103676930 loops=1)
                                 Buffers: shared hit=954918 read=221969
                           ->  Seq Scan on condition_occurrence_yrpart_2009  (cost=10000000000.00..10003024874.44 rows=114743696 width=17) (actual time=0.142..20191.131 rows=114743002 loops=1)
                                 Buffers: shared hit=1303719
                           ->  Seq Scan on condition_occurrence_yrpart_2010  (cost=10000000000.00..10001864406.36 rows=70720224 width=17) (actual time=0.050..12464.117 rows=70719679 loops=1)
                                 Buffers: shared hit=803603
                           ->  Seq Scan on condition_occurrence_yrpart_2011  (cost=10000000000.00..10000000014.95 rows=330 width=17) (actual time=0.022..0.022 rows=0 loops=1)

我还大量配置了我的 postgresql 以尝试将所有数据放入内存中,包括:

shared_buffers = 80GB
work_mem = 32GB
max_worker_processes = 32 
max_parallel_workers_per_gather = 16
max_parallel_workers = 32
wal_compression = on
max_wal_size = 8GB
enable_seqscan = off
enable_partitionwise_join = on
enable_partitionwise_aggregate = on
parallel_tuple_cost = 0.01
parallel_setup_cost = 100.0
shared_preload_libraries = 'pg_prewarm'
effective_cache_size = 192GB

我正在运行的虚拟机非常庞大。 256 GB 内存,32 核。 SSD 是 postgres 目录所在的位置...

这里有几个问题:

  1. 为什么这么慢?
  2. 为什么不并行运行?
  3. 尽管有 pg_prewarm,为什么我再次运行时性能没有提高?
  4. 为什么我的会话结束时会释放内存?我正在使用预热?

【问题讨论】:

    标签: postgresql performance explain


    【解决方案1】:

    我会这样重写查询:

    SELECT date_trunc('month', condition_start_date::timestamp) as dt
          , condition_source_value
          , CNT
      FROM (
           SELECT CONDITION_START_DATE
                , condition_source_value
                , COUNT(PERSON_ID) AS CNT
             FROM (SELECT CONDITION_START_DATE, CONDITION_SOURCE_VALUE, PERSON_ID
                     FROM synpuf5.condition_occurrence_yrpart
                  GROUP BY CONDITION_START_DATE, CONDITION_SOURCE_VALUE, PERSON_ID) A
          GROUP BY CONDITION_START_DATE, CONDITION_SOURCE_VALUE
          ORDER BY COUNT(PERSON_ID) DESC 
          LIMIT 10) B;
    

    我的答案与 JJANES 的答案几乎相同。但我认为你最好减少 DATE_TRUNC 函数调用的次数。在修改后的查询中,PostgreSQL 将只运行 DATE_TRUNC 函数 10 次。 以下是解释此查询的调整概念的 URL。 https://blog.naver.com/naivety1/222371990077

    【讨论】:

      【解决方案2】:

      这很慢,因为处理 3 亿行的数据需要一些时间。

      我认为它不是并行运行的,因为COUNT(DISTINCT...) 代码非常陈旧并且最近没有受到太多关注。它不知道如何使用哈希聚合,也不知道如何并行操作。 (在我手中,如果我将 parallel_tuple_cost 一直降低到零,它确实是并行运行的,但是聚集低于大规模排序并且没有任何好处。但我没有使用你的真实数据,所以可以得到不同的结果。)

      您可以通过分步执行 DISTINCT 和 COUNT 来解决 COUNT(DISTINCT...) 的不灵活问题:

      select dt, condition_source_value, count(person_id) from (
         SELECT distinct                                             
         date_trunc('month', condition_start_date::timestamp) as dt, 
         condition_source_value, 
         person_id                        
         FROM condition_occurrence_yrpart
      ) foo 
      GROUP BY dt, condition_source_value 
      ORDER BY COUNT(person_id) DESC LIMIT 10;
      

      不过,它仍然可能无法在正确的位置进行并行化。

      【讨论】:

        【解决方案3】:
        1. 为什么这么慢?

          对 3 亿行进行排序需要一段时间,即使是慷慨的 work_mem。超过 9 分钟的查询执行时间用于对GROUP BY 进行排序。

        2. 为什么不并行运行?

          因为在 PostgreSQL 中排序不能并行化。

        3. 尽管有 pg_prewarm,为什么我再次运行时性能没有提高?

          因为一切都已经被缓存了。

        4. 为什么我的会话结束时会释放内存?我正在使用预热?

          您的后端使用的内存肯定会在您的会话结束时被释放。用于shared_buffers 的内存不会被释放,因为这是数据库中所有进程共享的缓存。您不希望释放该内存。

        这是一个繁重的查询,需要一些时间。我认为这无法改进。

        您没有告诉我们分区表达式是什么,但由于它可能不是date_trunc('month', condition_start_date::timestamp),因此尽管有enable_partitionwise_aggregate = on,您也不会得到分区聚合。 PostgreSQL 不够聪明,无法推断它实际上可以做到这一点(假设您在 condition_start_date 上进行分区)。

        【讨论】:

          猜你喜欢
          • 2012-06-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-11-06
          • 1970-01-01
          • 2014-09-21
          • 1970-01-01
          相关资源
          最近更新 更多