【发布时间】:2021-10-14 09:29:17
【问题描述】:
我有一个非常简单的查询,它在 Postgres 11.12 上需要超过 10 分钟才能完成:
SELECT COUNT(r.*) as active_jobs
FROM project_raw_data r
INNER JOIN jobs j ON j.id = r.processing_job_id
WHERE r.ind_requires_processing = True AND
r.processing_error = False AND
r.processing_job_id IS NOT NULL AND
j.finished IS NULL AND
j.started IS NOT NULL;
project_raw_data 表有 ~50M 行,大小约为 500GB(其中有一些更大的元数据,此查询中涉及的列都是 boolean 或 timezone),jobs 表有~1M 行。
我在project_raw_data 表上应用了以下索引:
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_project_raw_data_processing_active ON
project_raw_data
USING btree (ind_requires_processing, processing_error, processing_job_id);
还有jobs 表上的这个:
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_studio_jobs_active ON
jobs
USING btree (id) WHERE ((finished IS NULL) AND (started IS NOT NULL));
如果我查看 PgAnalyze,索引应该已经是该查询的最佳索引:
所以我有点不太了解为什么这需要 10 分钟并从磁盘读取 70GB (!)(查看分析时)。
这是EXPLAIN ANALYZE 的输出:
Finalize Aggregate (cost=9860168.44..9860168.45 rows=1 width=8) (actual time=690627.757..690628.919 rows=1 loops=1)
-> Gather (cost=9860168.23..9860168.44 rows=2 width=8) (actual time=690625.874..690628.908 rows=3 loops=1)
Workers Planned: 2
Workers Launched: 2
-> Partial Aggregate (cost=9859168.23..9859168.24 rows=1 width=8) (actual time=690623.471..690623.475 rows=1 loops=3)
-> Hash Join (cost=97.43..9859165.28 rows=1180 width=1135) (actual time=690623.464..690623.467 rows=0 loops=3)
Hash Cond: (r.processing_job_id = j.id)
-> Parallel Seq Scan on project_raw_data r (cost=0.00..9845488.13 rows=5173208 width=1139) (actual time=690623.462..690623.463 rows=0 loops=3)
Filter: (ind_requires_processing AND (NOT processing_error) AND (processing_job_id IS NOT NULL))
Rows Removed by Filter: 16661364
-> Hash (cost=93.42..93.42 rows=321 width=4) (never executed)
-> Index Only Scan using idx_studio_jobs_active on jobs j (cost=0.27..93.42 rows=321 width=4) (never executed)
Heap Fetches: 0
Planning Time: 2.207 ms
Execution Time: 690629.645 ms
我们看到project_raw_data 表上的索引被完全跳过。但为什么呢?
【问题讨论】:
-
使用
count(*)有什么改变吗?或者至少count(r.ind_requires_processing)。该步骤的预期行数与实际行数之间存在巨大差异。尝试analyze project_raw_data ;或vacuum analyze project_raw_data ;更新统计信息 -
@a_horse_with_no_name 哇,实际上在本地表上运行
analyze使它使用索引......现在将在我们的生产数据库上运行它,看看是否有区别。我们在数据库上启用了 autovacuum,所以没想到会需要这个。希望这确实可以解决问题。 -
你的表有主键吗?他们有FK关系吗?这些是否受到约束/索引的支持?这些都应该在在非关键属性上添加任何额外索引之前定义。
-
@wildplasser,是的,两个表之间存在 PK 和 FK 关系。它们都有索引,每一列都应该被现有索引覆盖,如 PgAnalyze 屏幕截图所示(如果有帮助,可以显示表上的所有索引)。
-
对不起,我不看截图。 DDL 更容易阅读。顺便说一句:你知道
r.processing_job_id IS NOT NULL条件是多余的吗?
标签: postgresql