Postgres查询优化(强制索引扫描)

时间:2013-01-28 01:34:48

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

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

顺便说一下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[]))

4 个答案:

答案 0 :(得分:40)

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

SET enable_seqscan = OFF;

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

通过将PRIMARY KEY中的列顺序(以及用于在窗帘后面实现它的索引)切换到(t, metric_id),您可能会获得更好的结果。或者使用相反的列创建附加索引。

您通常不必通过手动干预强制执行更好的查询计划。如果将enable_seqscan = OFF设置为很多更好的计划,则可能在您的数据库中存在某些问题。考虑这个相关的答案:

答案 1 :(得分:1)

在这种情况下,您无法强制进行索引扫描,因为它不会使速度更快。

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

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

此外,您应确保在metrics (id)上有索引。

答案 2 :(得分:0)

看来你缺乏合适的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表)

答案 3 :(得分:0)

您是否尝试过使用:

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

喜欢here