如何加快选择特色?


16

我对某些时间序列数据有一个简单的选择:

SELECT DISTINCT user_id
FROM events
WHERE project_id = 6
AND time > '2015-01-11 8:00:00'
AND time < '2015-02-10 8:00:00';

而且需要112秒。这是查询计划:

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

我的应用程序必须执行许多不同的操作,并像这样计数。有没有更快的方法来获取此类数据?

Answers:


19

您可能不想听到此消息,但是加快速度的最佳选择SELECT DISTINCT避免 DISTINCT从头开始。在许多情况下(不是全部!),可以通过更好的数据库设计或更好的查询来避免这种情况。

有时,GROUP BY速度更快,因为它采用了不同的代码路径。

您的具体情况下,似乎无法摆脱DISTINCT。但是,如果您有许多此类查询,则可以使用专门的索引来支持查询:

CREATE INDEX foo ON events (project_id, "time", user_id);

user_id仅当您从中获得仅索引扫描时,添加才有用。点击链接获取详细信息。从您的查询计划中删除昂贵的位图堆扫描,这将占用90%的查询时间。

您的EXPLAIN输出告诉我,查询必须压缩半百万个匹配行中的2,491个不同的用户。无论您做什么,这都不会变得很快,但是它可能会更快。

如果查询中的时间间隔始终相同,则每MATERIALIIZED VIEW折叠user_id一次(project_id, <fixed time intervall>)将有很大帮助。但是,在不同的时间间隔内没有机会。也许您每小时至少可以折回一个用户或其他一些最小时间单位,这将购买足够的性能以保证可观的开销。

Nitpick:
最有可能的谓词"time"应该确实是:

AND "time" >= '2015-01-11 8:00:00'
AND "time" <  '2015-02-10 8:00:00';

除了:
不要time用作标识符。它是标准SQL中的保留字,而在Postgres中是基本类型。


我已经读了一些有关仅索引扫描的信息,我会试一试。
2015年

不幸的是,时间间隔不是固定的。
2015年

@Sam:那么您的示例查询通过建议的索引获得了多快的速度?
Erwin Brandstetter

3
@edwin:尚未尝试生产。但是,我在本地(具有相同数据)上运行了原始查询,这花费了3678.780毫秒。然后,我添加了索引,它最多加速了170.156毫秒。计划现在包含“在事件上使用foo进行仅索引扫描”。
2015年

1
@Sam:太好了!这就是我的目标。
Erwin Brandstetter

2

这是我对山姆案和欧文案答案的检验

drop table t1
create table t1 (id int, user_id int, project_id int, date_time timestamp without time zone) ;

insert into t1 -- 10 million row - size="498 MB"
select row_number() over(), round(row_number() over()/1000), round(row_number() over()/100000) , date
from generate_series('2015-01-01'::date, '2016-12-01'::date,'6 seconds'::interval
) date 
limit 10000000

-- before indexing - 10000000 row - output=100 row - time=2900ms
SELECT DISTINCT user_id
FROM t1
WHERE project_id = 1
AND date_time > '2015-01-01 8:00:00'
AND date_time < '2016-12-01 8:00:00' ;

CREATE INDEX foo ON t1 (project_id, date_time, user_id); -- time process=51.2 secs -- size="387 MB"         

-- after indexing - 10000000 row - output=100 row - time= 75ms (reduce ~ 38 times)
SELECT DISTINCT user_id
FROM t1
WHERE project_id = 1
AND date_time > '2015-01-01 00:00:00'
AND date_time < '2016-12-01 00:00:00' ;

欧文说:“您可能不希望听到此消息,但是加快SELECT DISTINCT的最佳选择是避免以DISTINCT开头。在许多情况下(并非全部!),可以通过更好的数据库设计或更好的查询来避免“。我认为他是对的,我们应该避免使用“区别,分组,排序”(如果有)。

我遇到了Sam的情况,我认为Sam可以按月在事件表上使用分区。查询时会减少数据量,但是需要一个函数(pl / pgsql)来执行,而不是上面的查询。该函数将找到适当的分区(取决于条件)以执行query。


2
>我认为他是对的,我们应该避免使用“区别,分组,排序” —以及SELECT,INSERT和UPDATE。如果我们避免这些构造,我们的数据库将非常快!
greatvovan '18 -10-25
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.