在大桌面上的第一次查询调用非常缓慢

Ger*_*fer 4 postgresql configuration query-optimization postgresql-9.2 postgresql-performance

我有一个问题,感觉它需要花费更多的时间.这仅适用于给定参数集的第一个查询,因此在缓存时没有问题.

我不确定会发生什么,但是,考虑到设置和设置,我希望有人可以对几个问题有所了解,并提供一些有关如何加快查询速度的见解.该表格相当大,Postgres估计其中大约155963000(14 GB).

询问

    select ts, sum(amp) as total_amp, sum(230 * factor) as wh
    from data_cbm_aggregation_15_min
    where virtual_id in (1818) and ts between '2015-02-01 00:00:00' and '2015-03-31 23:59:59'
    and deleted is null
    group by ts
    order by ts
Run Code Online (Sandbox Code Playgroud)

当我开始研究这个查询时花了大约15秒,经过一些更改后我已经达到了大约10秒,这对于像这样的简单查询似乎仍然很长.下面是结果explain analyze: http://explain.depesz.com/s/97V1.请注意GroupAggregate返回相同行数的原因是此示例仅virtual_id使用了一行,但可能会有更多行.

表和索引

正在查询的表,它每15分钟插入一次值

CREATE TABLE data_cbm_aggregation_15_min (
  virtual_id integer NOT NULL,
  ts timestamp without time zone NOT NULL,
  amp real,
  recs smallint,
  min_amp real,
  max_amp real,
  deleted boolean,
  factor real DEFAULT 0.25,
  min_amp_ts timestamp without time zone,
  max_amp_ts timestamp without time zone
)

ALTER TABLE data_cbm_aggregation_15_min ALTER COLUMN virtual_id SET STATISTICS 1000;
ALTER TABLE data_cbm_aggregation_15_min ALTER COLUMN ts SET STATISTICS 1000;
Run Code Online (Sandbox Code Playgroud)

查询中使用的索引

CREATE UNIQUE INDEX idx_data_cbm_aggregation_15_min_virtual_id_ts
ON data_cbm_aggregation_15_min USING btree (virtual_id, ts DESC);

ALTER TABLE data_cbm_aggregation_15_min
CLUSTER ON idx_data_cbm_aggregation_15_min_virtual_id_ts;
Run Code Online (Sandbox Code Playgroud)

Postgres设置

其他设置是默认设置.

default_statistics_target = 100 
maintenance_work_mem = 2GB 
effective_cache_size = 11GB
work_mem = 256MB
shared_buffers = 3840MB
random_page_cost = 1
Run Code Online (Sandbox Code Playgroud)

我试过了什么

在您发布https://wiki.postgresql.org/wiki/Slow_Query_Questions之前,我一直在关注这些事情,结果更详细如下:

  1. 摆弄Postgres设置,主要是random_page_cost自索引扫描以来降低,虽然它看起来不太特别,但是当它random_page_cost更高时,它会尝试做的位图堆扫描前面几英里.
  2. 将增加的统计信息添加到索引和条件所基于的列virtual_idtsWHERE.更改后,查询计划程序的估计行数更接近实际行数.
  3. the idx_data_cbm_aggregation_15_min_virtual_id_ts索引上的聚类似乎没有太大变化,而不是我注意到的.
  4. VACUUM手动运行没有太大变化,我已经运行autovacuum所以这并不奇怪.
  5. REINDEX在索引上运行大大缩减了(差不多50%!)但它没有提高速度.

Erw*_*ter 5

对夫妇的小改进

SELECT ts, sum(amp) AS total_amp, sum(factor) * 230  AS wh
FROM   data_cbm_aggregation_15_min
WHERE  virtual_id = 1818
AND    ts >= '2015-02-01 00:00'
AND    ts <  '2015-04-01 00:00'
AND    deleted IS NULL
GROUP  BY ts
ORDER  BY ts;
Run Code Online (Sandbox Code Playgroud)
  • sum(230 * factor)- 将总和乘以一次而不是乘以每个元素会更便宜:sum(factor) * 230结果是相同的,即使是NULL值也是如此.

  • ts between '2015-02-01 00:00:00' and '2015-03-31 23:59:59'可能不正确.要包括2015年3月的所有内容,请使用提供的替代方案.无论如何BETWEEN被翻译ts >= lower AND ts <= upper.它总是略快拼出来.

  • virtual_id in (1818)只是一种不必要的复杂的说法virtual_id = 1818.

更好的指数,可能更大的改善

CREATE INDEX data_cbm_aggregation_15_min_special_idx
ON data_cbm_aggregation_15_min (virtual_id, ts, amp, factor)
WHERE deleted IS NULL;
Run Code Online (Sandbox Code Playgroud)
  • 我在你的问题中没有看到DESC你的原始索引中的任何内容.虽然Index Scan Backward几乎和平原一样快Index Scan,但放弃修饰符仍然更好.

  • 最重要的是,自Postgres 9.2以来,只有索引扫描.我附加的两个索引列(amp,factor)仅在您从中获得仅索引扫描时才有意义.

  • 由于您显然对已删除的行不感兴趣,因此将其设为部分索引.只有在表格中有多个已删除的行时才需要付费.
    如果您可以排除表的其他大部分,请添加更多条件 - 并记住在查询中重复条件(即使看起来多余),因此Postgres了解索引是适用的.

表定义

像这样重新排序表列将每行节省8个字节:

CREATE TABLE data_cbm_aggregation_15_min (
   virtual_id integer NOT NULL,
   recs smallint,
   deleted boolean,
   ts timestamp NOT NULL,
   amp real,
   min_amp real,
   max_amp real,
   factor real DEFAULT 0.25,
   min_amp_ts timestamp,
   max_amp_ts timestamp
);
Run Code Online (Sandbox Code Playgroud)

有关:

最重要的信息

  • 对于非常大的表,第一次查询调用可能实质上更昂贵,因为整个表不能被缓存.后续调用从填充的缓存中获利.Postgres缓存块,不一定是整个表.

  • 还有一件事对于第一次通话很重要.由于Postgres的MVCC模型,它必须保持可见性信息.当自上次写入操作以来第一次读取表的页面时,Postgres会机会性地更新可见性信息,这可能会为第一次访问带来一些额外的成本(并为后续调用提供大量帮助).更多在手册中.关于dba.SE的相关回答:

关于你到目前为止所尝试的内容

  • SET STATISTICS 1000对于ts并且virtual_id是一个很好的想法,但效果在很大程度上通过设置无效random_page_cost = 1,这基本上强制对此查询进行索引扫描.

  • random_page_cost = 1正在告诉Postgres,随机访问与顺序访问一样便宜.这对于(几乎)完全驻留在缓存中的DB是有意义的.对于像你这样的巨大表格的数据库,这个设置似乎太极端了(即使它让Postgres支持所需的索引扫描).设置为random_page_cost = 1.1或可能更高.

  • 对于您呈现的查询的第一次调用,位图索引扫描通常是一个很好的计划 - 用于在表中随机分布的数据.由于您就像对此查询所需的那样对表进行聚类,因此索引扫描更有效.问题是:你的桌子会保持聚集状态吗?

  • 您的设置work_mem和其他资源,取决于你有多少RAM,您的磁盘的速度,对访问模式,你通常有多少并发连接有,是什么在服务器上的其他程序争夺资源等work_mem = 256MB似乎过高.对于呈现的查询,您不需要那么多.将其设置为高可能实际上会损害性能,因为它减少了可用于缓存的RAM.

  • REINDEX之后不会冗余CLUSTER,因为无论如何都会重新创建所有索引.您必须REINDEX 群集之前运行,或者您在表上拥有大量写入权限才能再次获得如此大的膨胀.

各个

  • 升级到Postgres 9.4(或即将推出的9.5,目前为alpha).版本9.2现在已经有3年了,最新版本已经获得了很多改进.

  • 查询计划表明,没有什么实际汇总.rows=4,117从索引中读取并rows=4,117保留GroupAggregate.看起来行ts已经是唯一的?然后你可以完全删除聚合并使它变得简单SELECT......

  • 如果这只是一个误导性的EXPLAIN输出,并且您通常输出的行数比读取的少得多,那么MATERIALIZED VIEW索引为on ts将是另一种选择.特别是与Postgres 9.4相结合,介绍REFRESH MATERIALIZED VIEW CONCURRENTLY.