You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Heroku PostgreSQL 12.16仅索引扫描成本过高的原因与优化咨询

问题原因分析

从执行计划可明确慢查询的核心原因:

  1. 索引无法适配OR条件:现有index_users_on_external_id_and_email_and_uuid是(external_id, email, uuid)的复合索引,PostgreSQL仅能利用索引前缀external_id=18筛选数据,email='user@example.com' OR uuid='779c7963-67b2-43ea-b19b-028759a146dc'只能作为后置过滤条件执行。这导致数据库需扫描111万+行符合external_id=18的数据,再逐一校验匹配,是耗时的主因。
  2. 并行扫描额外开销:因需处理大量行,数据库启动了并行Worker,但并行协调成本加上每个Worker扫描数十万无匹配数据,进一步拉长了执行时间。
  3. VACUUM ANALYZE无效的原因:该操作仅解决统计信息过时、死元组过多问题,而你的问题根源是索引结构与查询模式不匹配,因此无法改善性能。
优化方案

方案1:创建针对性部分索引

针对查询的两个分支分别创建复合索引,让PostgreSQL通过Bitmap Or操作快速定位目标行:

-- 适配external_id+email的查询分支
CREATE INDEX idx_users_external_email ON users (external_id, email) WHERE email IS NOT NULL;
-- 适配external_id+uuid的查询分支
CREATE INDEX idx_users_external_uuid ON users (external_id, uuid) WHERE uuid IS NOT NULL;

创建完成后,数据库会自动组合两个索引查询,无需扫描大量无关数据。

方案2:拆分OR查询为UNION ALL

将原查询拆为两个独立子查询,分别利用对应索引,找到匹配结果后立即返回:

SELECT 1 AS one
FROM "users"
WHERE external_id = 18 AND email = 'user@example.com'
LIMIT 1
UNION ALL
SELECT 1 AS one
FROM "users"
WHERE external_id = 18 AND uuid = '779c7963-67b2-43ea-b19b-028759a146dc'
LIMIT 1
LIMIT 1;

外层LIMIT 1确保任一子查询找到结果就终止整个查询,避免不必要计算。

方案3:调整现有索引(可选)

若不想新增索引,可将现有索引替换为(external_id, email)和(external_id, uuid)的组合,但效果不如方案1的部分索引高效——部分索引可过滤NULL值,进一步缩小索引范围。

内容的提问来源于stack exchange,提问作者Oscar Rivas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 07:30:19