在postgres的一张大桌上按日期查询加速一组

zai*_*ius 12 sql database postgresql indexing

我有一张约有2000万行的表.为了论证,我们可以说表中有两列 - 一个id和一个时间戳.我想要计算每天的物品数量.这就是我现在所拥有的.

  SELECT DATE(timestamp) AS day, COUNT(*)
    FROM actions
   WHERE DATE(timestamp) >= '20100101'
     AND DATE(timestamp) <  '20110101'
GROUP BY day;
Run Code Online (Sandbox Code Playgroud)

没有任何索引,这需要大约30秒才能在我的机器上运行.这是解释分析输出:

 GroupAggregate  (cost=675462.78..676813.42 rows=46532 width=8) (actual time=24467.404..32417.643 rows=346 loops=1)
   ->  Sort  (cost=675462.78..675680.34 rows=87021 width=8) (actual time=24466.730..29071.438 rows=17321121 loops=1)
         Sort Key: (date("timestamp"))
         Sort Method:  external merge  Disk: 372496kB
         ->  Seq Scan on actions  (cost=0.00..667133.11 rows=87021 width=8) (actual time=1.981..12368.186 rows=17321121 loops=1)
               Filter: ((date("timestamp") >= '2010-01-01'::date) AND (date("timestamp") < '2011-01-01'::date))
 Total runtime: 32447.762 ms
Run Code Online (Sandbox Code Playgroud)

由于我看到了顺序扫描,我试图在日期聚合上编制索引

CREATE INDEX ON actions (DATE(timestamp));
Run Code Online (Sandbox Code Playgroud)

这将速度降低了约50%.

 HashAggregate  (cost=796710.64..796716.19 rows=370 width=8) (actual time=17038.503..17038.590 rows=346 loops=1)
   ->  Seq Scan on actions  (cost=0.00..710202.27 rows=17301674 width=8) (actual time=1.745..12080.877 rows=17321121 loops=1)
         Filter: ((date("timestamp") >= '2010-01-01'::date) AND (date("timestamp") < '2011-01-01'::date))
 Total runtime: 17038.663 ms
Run Code Online (Sandbox Code Playgroud)

我是这个整个查询优化业务的新手,我不知道接下来该做什么.有什么线索我怎么能让这个查询运行得更快?

- 编辑 -

看起来我正在达到指数的极限.这几乎是在这个表上运行的唯一查询(尽管日期的值发生了变化).有没有办法对表格进行分区?或者创建一个包含所有计数值的缓存表?还是其他任何选择?

a_h*_*ame 6

有没有办法对表格进行分区?

是的:http:
//www.postgresql.org/docs/current/static/ddl-partitioning.html

或者创建一个包含所有计数值的缓存表?还是其他任何选择?

当然可以创建"缓存"表.但这取决于您需要多长时间的结果以及需要的准确程度.

CREATE TABLE action_report
AS
SELECT DATE(timestamp) AS day, COUNT(*)
    FROM actions
   WHERE DATE(timestamp) >= '20100101'
     AND DATE(timestamp) <  '20110101'
GROUP BY day;

然后a SELECT * FROM action_report会及时给你你想要的东西.然后,您将安排一个cron作业来定期重新创建该表.

如果时间范围随每个查询而变化,或者该查询每天只运行一次,那么这种方法当然无济于事.

  • 对于@ g33kz0r以及任何现在发现这个问题的人,请查看物化视图 - 它们具有相同的效果,但是,如果是postgres 9.4,它们可以同时刷新,因此您永远不必有时间表被锁定/为空. (2认同)