在大型 table 上优化 SELECT count(*)

Optimizing SELECT count(*) on large table

在具有 64GB Ram 和 20 个线程的 PostgreSQL 14 上的大型 table 上的基本计数。存储是 NVME 磁盘。

问题:

EXPLAIN (ANALYZE, BUFFERS)
SELECT count(*) FROM public.product;
Finalize Aggregate  (cost=2691545.69..2691545.70 rows=1 width=8) (actual time=330901.439..330902.951 rows=1 loops=1)
  Buffers: shared hit=1963080 read=1140455 dirtied=1908 written=111146
  I/O Timings: read=36692.273 write=6548.923
  ->  Gather  (cost=2691545.27..2691545.68 rows=4 width=8) (actual time=330901.342..330902.861 rows=1 loops=1)
        Workers Planned: 4
        Workers Launched: 0
        Buffers: shared hit=1963080 read=1140455 dirtied=1908 written=111146
        I/O Timings: read=36692.273 write=6548.923
        ->  Partial Aggregate  (cost=2690545.27..2690545.28 rows=1 width=8) (actual time=330898.747..330898.757 rows=1 loops=1)
              Buffers: shared hit=1963080 read=1140455 dirtied=1908 written=111146
              I/O Timings: read=36692.273 write=6548.923
              ->  Parallel Index Only Scan using points on products  (cost=0.57..2634234.99 rows=22524114 width=0) (actual time=0.361..222958.361 rows=90993600 loops=1)
                    Heap Fetches: 46261956
                    Buffers: shared hit=1963080 read=1140455 dirtied=1908 written=111146
                    I/O Timings: read=36692.273 write=6548.923
Planning:
  Buffers: shared hit=39 read=8
  I/O Timings: read=0.398
Planning Time: 2.561 ms
JIT:
  Functions: 4
  Options: Inlining true, Optimization true, Expressions true, Deforming true
  Timing: Generation 0.691 ms, Inlining 104.789 ms, Optimization 24.169 ms, Emission 22.457 ms, Total 152.107 ms
Execution Time: 330999.777 ms

The workers planned is 4 but launched 0, is that normal?

当太多并发事务竞争有限数量的允许并行工作程序时,可能会发生这种情况。 The manual:

The number of background workers that the planner will consider using is limited to at most max_parallel_workers_per_gather. The total number of background workers that can exist at any one time is limited by both max_worker_processes and max_parallel_workers. Therefore, it is possible for a parallel query to run with fewer workers than planned, or even with no workers at all. The optimal plan may depend on the number of workers that are available, so this can result in poor query performance. If this occurrence is frequent, consider increasing max_worker_processes and max_parallel_workers so that more workers can be run simultaneously or alternatively reducing max_parallel_workers_per_gather so that the planner requests fewer workers.

您还可以优化整体性能以释放资源或获得更好的硬件(除了提升 max_parallel_workers)。

同样烦恼的是:

Heap Fetches: 46261956

90993600 行。这是太多的安慰。仅索引扫描不应该执行那么多堆提取。

这两种症状都表明大量并发写入访问(或长运行事务占用资源并使autovacuum无法完成其工作)。调查一下,and/or 调整每个 table autovacuum 设置 table product 更积极,以便列统计更有效和可见性映射可以跟上。参见:

此外,根据半途有效的 table 统计数据,(快得惊人!)估计 可能就足够了?参见:

  • Fast way to discover the row count of a table in PostgreSQL