45

以下是我的查询。我试图让它使用索引扫描,但它只会 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[]))
4

4 回答 4

81

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

SET enable_seqscan = OFF;

不要生产服务器上使用它。手册中的详细信息在这里。

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

通过将您PRIMARY KEY(以及用于在幕后实现它的索引)中的列顺序切换到(t, metric_id). 或者创建一个带有反向列的附加索引。

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

于 2013-01-28T04:50:36.263 回答
2

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

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

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

此外,您应该确保在metrics (id).

于 2013-01-28T04:00:47.517 回答
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 表)

于 2013-01-28T10:52:38.493 回答
0

您是否尝试过使用:

S.NAME = ANY (VALUES ('cpu'), ('mem')) 而不是 ARRAY

喜欢这里

于 2016-03-21T17:48:07.450 回答