【问题标题】:Postgres query optimization (forcing an index scan)Postgres 查询优化(强制索引扫描)
【发布时间】:2013-01-11 08:02:28
【问题描述】:

以下是我的查询。我试图让它使用索引扫描,但它只会 seq 扫描。

顺便说一句,metric_data 表有 1.3 亿行。 metrics 表有大约 2000 行。

metric_data 表列:

  metric_id integer
, t timestamp
, d double precision
, PRIMARY KEY (metric_id, t)

如何让这个查询使用我的 PRIMARY KEY 索引?

SELECT
    S.metric,
    D.t,
    D.d
FROM metric_data D
INNER JOIN metrics S
    ON S.id = D.metric_id
WHERE S.NAME = ANY (ARRAY ['cpu', 'mem'])
  AND D.t BETWEEN '2012-02-05 00:00:00'::TIMESTAMP
              AND '2012-05-05 00:00:00'::TIMESTAMP;

解释:

Hash Join  (cost=271.30..3866384.25 rows=294973 width=25)
  Hash Cond: (d.metric_id = s.id)
  ->  Seq Scan on metric_data d  (cost=0.00..3753150.28 rows=29336784 width=20)
        Filter: ((t >= '2012-02-05 00:00:00'::timestamp without time zone)
             AND (t <= '2012-05-05 00:00:00'::timestamp without time zone))
  ->  Hash  (cost=270.44..270.44 rows=68 width=13)
        ->  Seq Scan on metrics s  (cost=0.00..270.44 rows=68 width=13)
              Filter: ((sym)::text = ANY ('{cpu,mem}'::text[]))

【问题讨论】:

    标签: postgresql indexing query-optimization postgresql-9.1 postgresql-performance


    【解决方案1】:

    出于测试目的,您可以通过“禁用”顺序扫描来强制使用索引 - 最好只在当前会话中使用:

    SET enable_seqscan = OFF;
    

    不要不要在生产服务器上使用它。 Details in the manual here.

    我引用了“禁用”,因为您实际上无法禁用顺序表扫描。但是对于 Postgres,任何其他可用选项现在都更可取。这将证明(metric_id, t) 上的多列索引可以使用 - 只是不如前导列上的索引有效。

    通过将PRIMARY KEY(以及用于在幕后实现它的索引)中的列顺序切换为(t, metric_id),您可能会获得更好的结果。或者创建一个 additional 索引,其中包含类似的反转列。

    您通常不必通过手动干预来强制执行更好的查询计划。如果设置enable_seqscan = OFF 会导致很多 更好的计划,那么您的数据库中可能有问题。考虑这个相关的答案:

    【讨论】:

    • 设置这个标志使得上面的查询在 150 毫秒内运行,而在我的机器上运行 45 秒。谢谢!
    • 非常有启发性的答案。和令人难以置信的结果。
    • @Jeff:我在回答中添加了另一个提示。
    • 感谢您的见解。应该是enable_seqscan = OFF,而不是最后一句中的enable_seq_scan = OFF
    • @mulhumu:谢谢,已修复。
    【解决方案2】:

    在这种情况下,您不能强制执行索引扫描,因为它不会使其更快。

    您当前在metric_data (metric_id, t) 上有索引,但服务器无法利用此索引进行查询,因为它需要能够仅通过metric_data.t 区分(没有metric_id),但没有这样的索引.服务器可以在复合索引中使用子字段,但只能从头开始。例如,通过metric_id 搜索将能够使用此索引。

    如果您在 metric_data (t) 上创建另一个索引,您的查询将使用该索引并且运行速度会更快。

    另外,您应该确保在metrics (id) 上有一个索引。

    【讨论】:

    • 这不太正确。多列索引也可以单独用于第二个字段。即使没有那么有效。考虑一下related question on dba.SE
    【解决方案3】:

    您似乎缺少合适的 FK 约束:

    CREATE TABLE metric_data
    ( metric_id integer
    , t timestamp
    , d double precision
    , PRIMARY KEY (metric_id, t)
    , FOREIGN KEY metrics_xxx_fk (metric_id) REFERENCES metrics (id)
    )
    

    在表格指标中:

    CREATE TABLE metrics
    ( id INTEGER PRIMARY KEY
    ...
    );
    

    还要检查您的统计数据是否足够(并且足够细粒度,因为您打算选择 0.2 % 的 metrics_data 表)

    【讨论】:

      【解决方案4】:

      你试过用吗:

      WHERE S.NAME = ANY (VALUES ('cpu'), ('mem')) 代替 数组

      点赞here

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-03-24
        • 2022-01-15
        • 1970-01-01
        • 1970-01-01
        • 2012-06-19
        • 1970-01-01
        • 1970-01-01
        • 2021-06-23
        相关资源
        最近更新 更多