Postgres查询优化的限制(已经使用仅索引扫描)

时间:2015-11-04 16:11:10

标签: postgresql

我有一个已经优化的Postgres查询,但我们在峰值负载下达到了100%的CPU使用率,所以我想看看在优化数据库方面是否还有更多工作要做互动。它已经在连接中使用了两个仅索引扫描,所以我的问题是在Postgres方面还有更多的事情要做。

该数据库是运行9.4.1的亚马逊托管的Postgres RDS db.m3.2xlarge实例(8个vCPU和30 GB内存),以下结果来自CPU使用率低且连接最少的时间段(大约15个)。峰值使用量大约是300个并发连接,当我们最大化CPU时(这会影响所有内容的性能)。

这是查询和EXPLAIN:

查询:

EXPLAIN (ANALYZE, BUFFERS) 

SELECT m.valdate, p.index_name, m.market_data_closing, m.available_date
FROM md.market_data_closing m 
JOIN md.primitive p on (m.primitive_id = p.index_id)
where p.index_name = ?
order by valdate desc

;

输出:

Sort  (cost=183.80..186.22 rows=967 width=44) (actual time=44.590..54.788 rows=11133 loops=1)
  Sort Key: m.valdate
  Sort Method: quicksort  Memory: 1254kB
  Buffers: shared hit=181
  ->  Nested Loop  (cost=0.85..135.85 rows=967 width=44) (actual time=0.041..32.853 rows=11133 loops=1)
        Buffers: shared hit=181
        ->  Index Only Scan using primitive_index_name_index_id_idx on primitive p  (cost=0.29..4.30 rows=1 width=25) (actual time=0.018..0.019 rows=1 loops=1)
              Index Cond: (index_name = '?'::text)
              Heap Fetches: 0
              Buffers: shared hit=3
        ->  Index Only Scan using market_data_closing_primitive_id_valdate_available_date_mar_idx on market_data_closing m  (cost=0.56..109.22 rows=2233 width=27) (actual time=0.016..12.059 rows=11133 loops=1)
              Index Cond: (primitive_id = p.index_id)
              Heap Fetches: 42
              Buffers: shared hit=178
Planning time: 0.261 ms
Execution time: 64.957 ms

以下是表格大小:

  • md.primitive:14283行
  • md.market_data_closing:13544087 rows

供参考,以下是表和索引的基本规范:

CREATE TABLE md.primitive(
    index_id serial NOT NULL,
    index_name text NOT NULL UNIQUE,
    index_description text not NULL,
    index_source_code text NOT NULL DEFAULT 'MAN',
    index_source_spec json NOT NULL DEFAULT '{}',
    frequency text NULL,
    primitive_type text NULL,
    is_maintained boolean NOT NULL default true,
    create_dt timestamp NOT NULL,
    create_user text NOT NULL,
    update_dt timestamp not NULL,
    update_user text not NULL,
 PRIMARY KEY  
(
    index_id 
)
) ;

CREATE INDEX ON md.primitive
(
    index_name ASC,
    index_id ASC
);

CREATE TABLE md.market_data_closing(
    valdate timestamp NOT NULL,
    primitive_id int references md.primitive,
    market_data_closing decimal(28, 10) not NULL,
    available_date timestamp NULL,
    pricing_source text not NULL,
    create_dt timestamp NOT NULL,
    create_user text NOT NULL,
    update_dt timestamp not NULL,
    update_user text not NULL,
 PRIMARY KEY  
(
    valdate,
    primitive_id
)
) ;

CREATE INDEX ON md.market_data_closing
(
    primitive_id ASC,
    valdate DESC,
    available_date DESC,
    market_data_closing ASC
);

还能做些什么?

2 个答案:

答案 0 :(得分:0)

似乎嵌套循环占用了大量的时间,而原始表只返回一行。您可以通过执行以下操作来尝试消除嵌套循环:

SELECT m.valdate, m.market_data_closing, m.available_date
FROM md.market_data_closing m 
WHERE m.primitive_id = (SELECT p.index_id
                        FROM md.primitive p
                        WHERE p.index_name = ?
                        OFFSET 0 -- probably not needed, try it)
ORDER BY valdate DESC;

这不会返回p.index_name,但可以通过将其选为const来轻松修复。

答案 1 :(得分:0)

对于下一代阅读此书:问题似乎出在索引

md.market_data_closing(
...
PRIMARY KEY  
    (
    valdate,
    primitive_id
    )

这似乎是错误的索引。应该是:

md.market_data_closing(
...
PRIMARY KEY  
    (
    primitive_id,
    valdate
    )

说明原因。这种查询:

...
JOIN md.primitive p on (m.primitive_id = p.index_id)
...

仅当Primitive_id是第一个字段时才有效。 还有

order by validate

如果validate是第二位,它将更加有效。 为什么?

因为索引是B树结构。

(
valdate,
primitive_id
)

产生

valdate1
    primitive_id1
    primitive_id2
    primitive_id3

valdate2
    primitive_id1
    primitive_id2

使用此树,您无法有效地按Primitive_id1搜索 但是

(
primitive_id,
valdate
)

产生

primitive_id1
    valdate1
    valdate2
    valdate3

primitive_id2
    valdate1
    valdate2

这对于按原始ID查找有效。 此问题还有另一种解决方案: 如果您不想更改索引,请在valdate上添加严格相等的条件。 说“ valdate = some_date”,这将使您的索引有效。