Postgres查询速度慢,只有索引扫描

时间:2017-08-09 03:16:21

标签: postgresql

我有一个表call_logs,它包含一个id,device_id,timestamp变量以及其他一些字段。 我目前正在尝试编写一个返回最后一个调用的查询,如果它适用于每个设备。 目前我的查询是:

SELECT DISTINCT ON (device_id) c.device_id, c.timestamp, c.working, c.id
FROM call_logs c
ORDER BY c.device_id, c.timestamp desc;

并返回我想要的信息。 但我的生产服务器现在变得相当大,我在表中有大约6,000,000条记录。

我在此表中添加了一个索引:

CREATE INDEX cl_device_timestamp
ON public.call_logs USING btree
(device_id, timestamp DESC, id, working)
TABLESPACE pg_default;

但我得到了我认为非常慢的时间: 以下是对查询的解释分析:

EXPLAIN ANALYSE SELECT DISTINCT ON (device_id) c.device_id, c.timestamp, c.working, c.id
                                                     FROM call_logs c
                                                      ORDER BY c.device_id, c.timestamp desc;
    QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
 Unique  (cost=0.56..363803.37 rows=120 width=25) (actual time=0.069..2171.201 rows=124 loops=1)
   ->  Index Only Scan using cl_device_timestamp on call_logs c  (cost=0.56..347982.87 rows=6328197 width=25) (actual time=0.067..1594.953 rows=6331024 loops=1)
         Heap Fetches: 8051
 Planning time: 0.184 ms
 Execution time: 2171.281 ms
(5 rows)

我只有124个唯一的device_id。我不会想到这对索引来说会是一个缓慢的过程吗?出了什么问题?或者为什么这么慢?

2 个答案:

答案 0 :(得分:1)

我最终这样做了:

Unique  (cost=607.92..608.06 rows=11 width=25) (actual time=3.291..3.344 rows=117 loops=1)
   ->  Sort  (cost=607.92..607.95 rows=11 width=25) (actual time=3.289..3.310 rows=117 loops=1)
         Sort Key: d.id, c."timestamp", c.id, c.working
         Sort Method: quicksort  Memory: 34kB
         ->  Nested Loop  (cost=1.05..607.73 rows=11 width=25) (actual time=0.057..3.162 rows=117 loops=1)
               ->  Seq Scan on devices d  (cost=0.00..4.18 rows=118 width=8) (actual time=0.006..0.029 rows=119 loops=1)
               ->  Index Only Scan using cl_device_timestamp on call_logs c  (cost=1.05..5.10 rows=1 width=25) (actual time=0.007..0.007 rows=1 loops=119)
                     Index Cond: ((device_id = d.id) AND ("timestamp" = (SubPlan 2)))
                     Heap Fetches: 110
                     SubPlan 2
                       ->  Result  (cost=0.48..0.49 rows=1 width=8) (actual time=0.018..0.018 rows=1 loops=119)
                             InitPlan 1 (returns $1)
                               ->  Limit  (cost=0.43..0.48 rows=1 width=8) (actual time=0.017..0.017 rows=1 loops=119)
                                     ->  Index Only Scan Backward using test1 on call_logs t  (cost=0.43..2674.01 rows=52483 width=8) (actual time=0.017..0.017 rows=1 loops=119)
                                           Index Cond: ((device_id = d.id) AND ("timestamp" IS NOT NULL))
                                           Heap Fetches: 110
 Planning time: 0.645 ms
 Execution time: 3.461 ms
(18 rows)

最终变得更好

itertools.product

答案 1 :(得分:0)

你的索引是4列,而不是1列。根据四列数据分布中的一个,您无法估计复合索引的大小和效率。

接下来 - 您只有124个不同设备的事实并不意味着更快的索引。相反 - 不太明显的价值将树分割成较少的部分,因此部分更大。例如,一百万个bigint值上的bigserial有一百万个不同的值,并且确切的id得到了非常快。虽然布尔列索引扫描只有两(3)个值,因此需要更长的时间。

最后一个论点 - 确实是两秒非常慢。但考虑到你扫描600万行的事实,比较时间戳,2秒就变得完全可以接受了。

您可以牺牲OLTP速度并创建一些触发器,以便将每个设备的数据更改的上一个时间戳保存到某些外部表中。然后从短外部表中选择这样的预聚合值对于127个设备将花费微秒。