Sam*_*Sam 25 postgresql performance optimization postgresql-9.3 amazon-rds query-performance
我在一些时间序列数据上有一个简单的选择:
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';
Run Code Online (Sandbox Code Playgroud)
它需要112秒。这是查询计划:
http://explain.depesz.com/s/NTyA
我的应用程序必须执行许多不同的操作并像这样计数。有没有更快的方法来获取这种数据?
Erw*_*ter 27
您可能不想听到这个,但加快速度的最佳选择SELECT DISTINCT是避免 DISTINCT开始。在许多情况下(不是全部!)可以通过更好的数据库设计或更好的查询来避免。
有时,GROUP BY速度更快,因为它需要不同的代码路径。
在您的特定情况下,您似乎无法摆脱DISTINCT(好吧,请参阅打击)。但是,如果您有许多此类查询,则可以使用特殊索引支持查询:
CREATE INDEX foo ON events (project_id, "time", user_id);
Run Code Online (Sandbox Code Playgroud)
user_id仅当您从中获得仅索引扫描时,添加才有用。点击链接了解详情。将从您的查询计划中删除昂贵的位图堆扫描,这会消耗 90% 的查询时间。
您EXPLAIN在 50 万行符合条件的行中显示了 2,491 个不同的用户。无论您做什么,这都不会变得非常快,但它可以大大加快。每个用户大约有 200 行,在上面的索引上模拟索引跳过扫描可能会付出代价。范围条件time使问题复杂化,每个用户 200 行仍然是一个适中的数字。所以不确定。看:
无论哪种方式,如果查询中的时间间隔始终相同,则MATERIALIZED VIEW折叠user_idper(project_id, <fixed time interval>)将大有帮助。但是,没有机会以不同的时间间隔存在。也许您至少可以每小时或其他最小时间单位折叠用户数,这将购买足够的性能来保证可观的开销。可以与任一查询样式结合使用。
Nitpick:
最有可能的谓词"time"应该是:
AND "time" >= '2015-01-11 8:00:00'
AND "time" < '2015-02-10 8:00:00';Run Code Online (Sandbox Code Playgroud)
旁白:
不要time用作标识符。它是标准 SQL 中的保留字,也是 Postgres 中的基本类型。
这是我对 Sam 的案例和Erwin 的答案的测试
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' ;
Run Code Online (Sandbox Code Playgroud)
Erwin 说:“你可能不想听到这个,但加速 SELECT DISTINCT 的最佳选择是从一开始就避免 DISTINCT。在许多情况下(不是全部!)可以通过更好的数据库设计或更好的查询来避免它”。我认为他是对的,我们应该避免使用“distinct、group by、order by”(如果有的话)。
我遇到了 Sam 的情况,我认为 Sam 可以按月对事件表使用分区。当您查询时,它会减少您的数据大小,但是您需要一个函数(pl/pgsql)来执行而不是上面的查询。该函数将找到适当的分区(取决于条件)来执行查询。
| 归档时间: |
|
| 查看次数: |
38759 次 |
| 最近记录: |