我如何提高Postgres select语句的速度?
我有以下表格:
CREATE TABLE views (
view_id bigint NOT NULL,
usr_id bigint,
ip inet,
referer_id bigint,
country_id integer,
validated smallint,
completed smallint,
value numeric
);
ALTER TABLE ONLY views
ADD CONSTRAINT "Views_pkey" PRIMARY KEY (view_id);
CREATE TABLE country (
country_id integer NOT NULL,
country character varying(2)
);
ALTER TABLE ONLY country
ADD CONSTRAINT country_pkey PRIMARY KEY (country_id);
CREATE TABLE file_id_view_id (
file_id bigint,
view_id bigint,
created_ts timestamp without time zone
);
CREATE TABLE file_owner (
file_id bigint NOT NULL,
owner_id bigint
);
ALTER TABLE ONLY file_owner
ADD CONSTRAINT owner_table_pkey PRIMARY KEY (file_id);
CREATE TABLE referer (
referer_id bigint NOT NULL,
referer character varying(255)
);
ALTER TABLE ONLY referer
ADD CONSTRAINT referer_pkey PRIMARY KEY (referer_id);
views
和file_id_view_id
表每个都有大约340M行。 每小时他们将增加60万行。
file_owner
表格有75K行,每小时增加100行。
country
表格有233行,很少有变化。
referer
表有6494行,很少发生变化。
我的目标是能够执行一个查询,如:
SELECT Count(ft.*) AS total_views,
( Count(ft.*) - SUM(ft.valid) ) AS invalid_views,
SUM(ft.valid) AS valid_views,
SUM(ft.values) AS VALUES,
ft.day AS day,
( CASE
WHEN r.referer IS NULL THEN 'Unknown'
ELSE r.referer
END ) AS referer,
( CASE
WHEN c.country IS NULL THEN 'Unknown'
ELSE c.country
END ) AS country
FROM country c
right join (referer r
right join (SELECT v.validated AS valid,
v.value AS VALUES,
vf.day AS day,
vf.view_id AS view_id,
v.referer_id AS referer_id,
v.country_id AS country_id
FROM VIEWS v,
(SELECT view_id,
fivi.created_ts :: timestamp :: DATE AS
day
FROM file_id_view_id fivi
join (SELECT file_id
FROM file_owner
WHERE owner_id = 75
GROUP BY file_id) fo
ON ( fo.file_id = fivi.file_id )
WHERE ( fivi.created_ts BETWEEN
'2015-11-01' AND '2015-12-01' )
GROUP BY view_id,
day) vf
WHERE v.view_id = vf.view_id) ft
ON ( ft.referer_id = r.referer_id ))
ON ( ft.country_id = c.country_id )
GROUP BY day,
referer,
country;
生产:
total_views | invalid_views | valid_views | values | day | referer | country
------------+---------------+-------------+--------+------------+-----------------+---------
使用EXPLAIN ANALYZE
运行此类查询时,将生成以下内容:
GroupAggregate (cost=38893491.99..40443007.61 rows=182295955 width=52) (actual time=183725.696..205882.889 rows=172 loops=1)
Group Key: ((fivi.created_ts)::date), r.referer, c.country
-> Sort (cost=38893491.99..38984639.97 rows=182295955 width=52) (actual time=183725.655..200899.098 rows=8390217 loops=1)
Sort Key: ((fivi.created_ts)::date), r.referer, c.country
Sort Method: external merge Disk: 420192kB
-> Hash Left Join (cost=16340128.88..24989809.75 rows=182295955 width=52) (actual time=23399.900..104337.332 rows=8390217 loops=1)
Hash Cond: (v.country_id = c.country_id)
-> Hash Left Join (cost=16340125.36..24800637.72 rows=182295955 width=49) (actual time=23399.782..102534.655 rows=8390217 loops=1)
Hash Cond: (v.referer_id = r.referer_id)
-> Merge Join (cost=16340033.52..24051874.62 rows=182295955 width=29) (actual time=23397.410..99955.000 rows=8390217 loops=1)
Merge Cond: (fivi.view_id = v.view_id)
-> Group (cost=16340033.41..16716038.36 rows=182295955 width=16) (actual time=23397.298..30454.444 rows=8390217 loops=1)
Group Key: fivi.view_id, ((fivi.created_ts)::date)
-> Sort (cost=16340033.41..16434985.73 rows=189904653 width=16) (actual time=23397.294..28165.729 rows=8390217 loops=1)
Sort Key: fivi.view_id, ((fivi.created_ts)::date)
Sort Method: external merge Disk: 180392kB
-> Nested Loop (cost=6530.43..8799350.01 rows=189904653 width=16) (actual time=63.123..15131.956 rows=8390217 loops=1)
-> HashAggregate (cost=6530.31..6659.62 rows=43104 width=8) (actual time=62.983..90.331 rows=43887 loops=1)
Group Key: file_owner.file_id
-> Bitmap Heap Scan on file_owner (cost=342.90..6508.76 rows=43104 width=8) (actual time=5.407..50.779 rows=43887 loops=1)
Recheck Cond: (owner_id = 75)
Heap Blocks: exact=5904
-> Bitmap Index Scan on owner_id_index (cost=0.00..340.74 rows=43104 width=0) (actual time=4.327..4.327 rows=45576 loops=1)
Index Cond: (owner_id = 75)
-> Index Scan using file_id_view_id_indexing on file_id_view_id fivi (cost=0.11..188.56 rows=4406 width=24) (actual time=0.122..0.306 rows=191 loops=43887)
Index Cond: (file_id = file_owner.file_id)
Filter: ((created_ts >= '2015-11-01 00:00:00'::timestamp without time zone) AND (created_ts <= '2015-12-01 00:00:00'::timestamp without time zone))
Rows Removed by Filter: 184
-> Index Scan using "Views_pkey" on views v (cost=0.11..5981433.17 rows=338958763 width=25) (actual time=0.088..46804.757 rows=213018702 loops=1)
-> Hash (cost=68.77..68.77 rows=6591 width=28) (actual time=2.344..2.344 rows=6495 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 410kB
-> Seq Scan on referer r (cost=0.00..68.77 rows=6591 width=28) (actual time=0.006..1.156 rows=6495 loops=1)
-> Hash (cost=2.70..2.70 rows=233 width=7) (actual time=0.078..0.078 rows=233 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 10kB
-> Seq Scan on country c (cost=0.00..2.70 rows=233 width=7) (actual time=0.005..0.042 rows=233 loops=1)
Planning time: 1.015 ms
Execution time: 206034.660 ms
(37 rows)
计划在explain.depesz.com上:http://explain.depesz.com/s/OiN
206s运行时间。
有些事情要注意,
Postgresql版本9.4
我已经调整了配置,如下所示:
目前存在以下索引:
上一个查询使用的是保守选择的所有者ID ,有些查询可能会导致file_id_view_id表的1/3与视图连接 。
改变数据结构是最后的手段。 在这个阶段,这种变化必须是由于严重的担忧。
只有在需要的情况下才可以将db视为只读,写入的数据每小时完成一次,并在每次写入后为Postgres提供充足的呼吸空间。 在600K小时写入的当前时刻,db在1100s内返回(这是由于插入成本以外的其他原因)。 如果增加读取速度,则有足够的空间添加附加索引,读取速度是优先考虑的。
硬件规格是:
CPU:http://ark.intel.com/products/83356/Intel-Xeon-Processor-E5-2630-v3-20M-Cache-2_40-GHz
内存: 128GB
存储: 1.5TB PCIE SSD
如何优化我的数据库或查询,以便我可以在合理的时间范围内检索我需要的数据库信息?
我可以做些什么来优化我目前的设计?
我相信Postgres及其运行的硬件具有比目前好得多的性能。
UPDATE
我努力了:
有没有人有任何经验重组表格这个大? 这可行吗? 这需要几天,几小时(估计当然)?
我正在考虑对数据库进行反规范化处理,因为它只会在此方法中引用。 我唯一担心的是 - 如果从索引owner_id的表中调用100M行,速度足够快还是仍然会面临相同的性能问题? 会讨厌以某种方式走回头路。
我正在研究的另一个解决方案是@ ivan.panasuik建议,将全天的数据分组到另一个表中,因为一旦过去了,信息是不变的,不需要更改或更新。 然而,我不确定如何顺利实现这一点 - 我应该在插入处于暂挂状态时通过数据查询运行并尽可能快地捕捉日期? 从那时起有触发器设置?
数据库的速度通常不是你的硬件,而是你如何使用引擎本身的智能和特性。
尽量避免次选 - 特别是在处理大量数据时。 这些通常无法通过查询规划器进行优化。 在大多数情况下,如果需要,您应该能够将简单的子查询转换为JOIN,甚至可以在手之前单独进行数据库查找。
对你的表进行分区 - PostgreSQL本身并不这么做(有点),但是如果你经常只访问最近的数据,你可以通过移动归档数据来消除很多工作。
考虑一个数据仓库策略 - 当你处理这些数据时,你应该考虑以非规范化的方式存储数据的副本,因为讨厌的JOIN已经被处理过了,所以很快就可以检索到。 我们使用Redshift(PostgeSQL的衍生产品)做到这一点,以便在运行报表时不需要执行任何JOIN。
它很难预测优化,而不尝试和尝试....所以一个一个地尝试。 还有祝你好运。
链接地址: http://www.djcxy.com/p/89181.html上一篇: How do I increase the speed of my Postgres select statement?