Postgres 使用 MAX 和 groupBy 索引查询

bur*_*nsy 8 postgresql index aggregate index-tuning postgresql-9.4

有没有办法索引以下查询?

SELECT run_id, MAX ( frame ) , MAX ( time ) FROM run.frames_stat GROUP BY run_id;
Run Code Online (Sandbox Code Playgroud)

我尝试在frameand上创建排序(非复合)索引time,并在 上创建索引run_id,但查询规划器不使用它们。

杂项信息:

  • 不幸的是(出于我不会进入的原因)我无法更改查询
  • frames_stat表有 4200 万行
  • 表格不变(不会发生进一步的插入/删除)
  • 查询总是很慢,只是变慢了,因为这个数据集比过去大。
  • 表上没有索引
  • 我们正在使用 Postgres 9.4
  • 数据库的“work_mem”大小为 128MB(如果相关)。
  • 硬件:130GB 内存,10 核至强

架构:

CREATE TABLE run.frame_stat (
  id bigint NOT NULL,
  run_id bigint NOT NULL,
  frame bigint NOT NULL,
  heap_size bigint NOT NULL,
  "time" timestamp without time zone NOT NULL,
  CONSTRAINT frame_stat_pkey PRIMARY KEY (id)
)
Run Code Online (Sandbox Code Playgroud)

解释分析:

HashAggregate  (cost=1086240.000..1086242.800 rows=280 width=24) (actual time=14182.426..14182.545 rows=280 loops=1)
  Group Key: run_id
  ->  Seq Scan on zulu  (cost=0.000..770880.000 rows=42048000 width=24) (actual time=0.037..4077.182 rows=42048000 loops=1)
Run Code Online (Sandbox Code Playgroud)

Erw*_*ter 10

太糟糕了

如果您根本无法更改查询,那就太糟糕了。你不会得到很好的解决方案。如果您没有对表 ( ) 进行表限定,您可以在另一个模式(或只是一个临时模式)中创建一个具有相同名称的物化视图(见下文)并调整(可选地仅在需要的会话中) -获得卓越的性能。run.frames_statsearch_path

这是这种技术的秘诀:

@ Joishi的想法RULE会(绝望),最后不得已的措施。但我宁愿不去那里。意外行为的陷阱太多。

更好的查询/索引

如果您可以更改查询,您应该尝试模拟松散索引扫描:

当基于每个相关一行run_id的单独时,这甚至更有效- 我们称之为run_tbl。如果您还没有它,请创建它!
使用相关子查询实现:

SELECT run_id
    , (SELECT frame
       FROM   run.frames_stat
       WHERE  run_id = r.run_id
       ORDER  BY frame DESC NULLS LAST
       LIMIT  1) AS max_frame
    , (SELECT "time"
       FROM   run.frames_stat
       WHERE  run_id = r.run_id
       ORDER  BY "time" DESC NULLS LAST
       LIMIT  1) AS max_time
FROM   run_tbl r;
Run Code Online (Sandbox Code Playgroud)

创建两个多列索引与匹配排序闪电的性能:

CREATE index fun_frame_idx ON run.frames_stat (run_id, frame DESC NULLS LAST);
CREATE index fun_frame_idx ON run.frames_stat (run_id, "time" DESC NULLS LAST);
Run Code Online (Sandbox Code Playgroud)

NULLS LAST只有在可以有空值时才需要。但它不会伤害任何方式。

只有 280 个 distinct run_id,这将非常快。

物化视图

或者,基于这些关键信息:

“frames_stat”表有 4200 万行

rows=280 -- 返回的行数=distinct run_id

表格不变(无插入/删除)

使用 a MATERIALIZED VIEW,它将很小(只有 280 行)并且速度非常快。
您仍然需要更改查询以使其基于 MV 而不是表。

旁白:永远不要使用(在标准 SQL 中)这样的保留字time作为标识符。