我有一个问题,感觉它需要花费更多时间。这仅适用于给定参数集的第一个查询,因此在缓存时没有问题。
我不确定会发生什么,但是,考虑到设置和设置,我希望有人可以对一些问题有所了解,并提供一些有关如何加快查询速度的见解。有问题的表格相当大,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
当我开始研究这个查询时花费了大约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;
查询中使用的索引
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;
其他设置是默认设置。
default_statistics_target = 100
maintenance_work_mem = 2GB
effective_cache_size = 11GB
work_mem = 256MB
shared_buffers = 3840MB
random_page_cost = 1
在https://wiki.postgresql.org/wiki/Slow_Query_Questions发帖之前,我一直在关注这些事情,结果更详细如下:
random_page_cost
,虽然看起来不太特别,但是当random_page_cost
更高时,它尝试做的位图堆扫描提前了几英里。 / LI>
virtual_id
条件所基于的ts
和WHERE
列。更改后,查询计划程序的估计行数更接近实际行数。the idx_data_cbm_aggregation_15_min_virtual_id_ts
索引上的聚类似乎没有太大变化,而不是我注意到的。VACUUM
没有太大变化,我已经在运行autovacuum所以这并不奇怪。REINDEX
大大缩减了(差不多50%!)但它没有提高速度。答案 0 :(得分: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;
- 将总和乘以而不是乘以每个元素会更便宜:sum(230 * factor)
sum(factor) * 230
结果是相同的,即使是NULL值。
可能不正确。要包含2015年3月的所有,请使用提供的替代方案。无论如何,ts between '2015-02-01 00:00:00' and '2015-03-31 23:59:59'
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;
我的问题中没有任何内容会在您的原始索引中显示DESC
。虽然Index Scan Backward
几乎与普通Index Scan
一样快,但删除修饰符仍然会更好。
最重要的是,自Postgres 9.2以来有index-only scans。我附加的两个索引列(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
);
相关:
对于非常大的表,第一次查询调用可能要贵得多,因为整个表不能被缓存。后续调用从填充的缓存中获利。 Postgres缓存块,不一定是整个表。
对于第一次调用而言,还有一件事可能很重要。由于Postgres的MVCC模型,它必须保持可见性信息。当自上次写入操作以来第一次读取表的页面时,Postgres会机会性地更新可见性信息,这可能会为第一次访问带来一些额外的成本(并为后续调用提供大量帮助)。 More in the manual here。关于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年了,最新版本已经有很多改进。
query plan表示 没有实际上已汇总。从索引中读取rows=4,117
,rows=4,117
后保留GroupAggregate
。看来ts
上的行已经是唯一的?然后,您可以完全删除聚合并使其成为简单的SELECT
...
如果这只是一个误导性的EXPLAIN
输出,并且您输出的行数通常少于读取的行数,那么索引为ts
的{{3}}将是另一种选择。特别是与Postgres 9.4结合使用,它引入了MATERIALIZED VIEW
。