优化一系列时间戳的查询(一列)

Eva*_*eby 9 postgresql performance index optimization postgresql-9.3 postgresql-performance

我正在通过 Heroku 使用 Postgres 9.3。

我有一个表,“交通”,有 100 万条记录,每天都有很多插入和更新。我需要在不同的时间范围内跨该表执行 SUM 运算,这些调用最多可能需要 40 秒,我很想听听有关如何改进它的建议。

我在这张桌子上有以下索引:

CREATE INDEX idx_traffic_partner_only ON traffic (dt_created) WHERE campaign_id IS NULL AND uuid_self <> uuid_partner;
Run Code Online (Sandbox Code Playgroud)

这是一个示例 SELECT 语句:

SELECT SUM("clicks") AS clicks, SUM("impressions") AS impressions
FROM "traffic"
WHERE "uuid_self" != "uuid_partner"
AND "campaign_id" is NULL
AND "dt_created" >= 'Sun, 29 Mar 2015 00:00:00 +0000'
AND "dt_created" <= 'Mon, 27 Apr 2015 23:59:59 +0000' 
Run Code Online (Sandbox Code Playgroud)

这是解释分析:

Aggregate  (cost=21625.91..21625.92 rows=1 width=16) (actual time=41804.754..41804.754 rows=1 loops=1)
  ->  Index Scan using idx_traffic_partner_only on traffic  (cost=0.09..20085.11 rows=308159 width=16) (actual time=1.409..41617.976 rows=302392 loops=1)
      Index Cond: ((dt_created >= '2015-03-29'::date) AND (dt_created <= '2015-04-27'::date))
Total runtime: 41804.893 ms
Run Code Online (Sandbox Code Playgroud)

http://explain.depesz.com/s/gGA

这个问题与 SE 上的另一个问题非常相似,但那个问题使用了跨两个列时间戳范围的索引,并且该查询的索引规划器的估计值相差甚远。主要建议是创建一个排序的多列索引,但对于没有太大影响的单列索引。其他建议是使用 CLUSTER / pg_repack 和 GIST 索引,但我还没有尝试过,因为我想看看是否有使用常规索引的更好解决方案。

优化对一系列时间戳的查询(两列)

作为参考,我尝试了以下数据库未使用的索引:

INDEX idx_traffic_2 ON traffic (campaign_id, uuid_self, uuid_partner, dt_created);
INDEX idx_traffic_3 ON traffic (dt_created);
INDEX idx_traffic_4 ON traffic (uuid_self);
INDEX idx_traffic_5 ON traffic (uuid_partner);
Run Code Online (Sandbox Code Playgroud)

编辑:Ran EXPLAIN (ANALYZE, VERBOSE, COSTS, BUFFERS) 这些是结果:

Aggregate  (cost=20538.62..20538.62 rows=1 width=8) (actual time=526.778..526.778 rows=1 loops=1)
  Output: sum(clicks), sum(impressions)
  Buffers: shared hit=47783 read=29803 dirtied=4
  I/O Timings: read=184.936
  ->  Index Scan using idx_traffic_partner_only on public.traffic  (cost=0.09..20224.74 rows=313881 width=8) (actual time=0.049..431.501 rows=302405 loops=1)
      Output: id, uuid_self, uuid_partner, impressions, clicks, dt_created... (other fields redacted)
      Index Cond: ((traffic.dt_created >= '2015-03-29'::date) AND (traffic.dt_created <= '2015-04-27'::date))
      Buffers: shared hit=47783 read=29803 dirtied=4
      I/O Timings: read=184.936
Total runtime: 526.881 ms
Run Code Online (Sandbox Code Playgroud)

http://explain.depesz.com/s/7Gu6

表定义:

CREATE TABLE traffic (
    id              serial,
    uuid_self       uuid not null,
    uuid_partner    uuid not null,
    impressions     integer NOT NULL DEFAULT 1,
    clicks          integer NOT NULL DEFAULT 0,
    campaign_id     integer,
    dt_created      DATE DEFAULT CURRENT_DATE NOT NULL,
    dt_updated      TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
)
Run Code Online (Sandbox Code Playgroud)

id 是主键,uuid_self、uuid_partner 和campaign_id 都是外键。dt_updated 字段使用 postgres 函数更新。

Cra*_*ger 2

在我看来,您正在查询大索引中的大量数据,因此速度很慢。那里没有什么明显的错误。

如果您使用的是 PostgreSQL 9.3 或 9.4,您可以尝试看看是否可以通过将其变成某种覆盖索引来获得仅索引扫描。

CREATE INDEX idx_traffic_partner_only 
ON traffic (dt_created, clicks, impressions)
WHERE campaign_id IS NULL 
  AND uuid_self <> uuid_partner;
Run Code Online (Sandbox Code Playgroud)

PostgreSQL 没有真正的覆盖索引或对索引项的支持,这些索引项只是值,而不是 b 树的一部分,因此这比使用这些功能可能更慢且更昂贵。如果Vacuum 运行得足够频繁以保持可见性映射最新,那么它仍然可能优于普通索引扫描。


理想情况下,PostgreSQL 将像 MS-SQL Server 一样支持索引中的辅助数据字段(此语法在 PostgreSQL 中不起作用):

-- This will not work in PostgreSQL (at least 9.5)
-- it's an example of what I wish did work. Don't
-- comment to say it doesn't work.
--
CREATE INDEX idx_traffic_partner_only 
ON traffic (dt_created)
INCLUDING (clicks, impressions) -- auxillary data columns
WHERE campaign_id IS NULL 
  AND uuid_self <> uuid_partner;
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

5849 次

最近记录:

10 年,4 月 前