Postgresql Postgres查询速度慢,仅扫描索引

Postgresql Postgres查询速度慢,仅扫描索引,postgresql,Postgresql,我有一个表call\u logs,它包含一个id、device\u 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; 它会返回我想要的信息。 但我的

我有一个表call\u logs,它包含一个id、device\u 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;
它会返回我想要的信息。 但我的生产服务器现在变得相当大,表中有大约6000000条记录

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

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个唯一的设备id。我不会想到这将是一个索引的缓慢过程?你知道怎么回事吗?或者为什么这么慢?

您的索引位于4列,而不是一列。您无法基于四列数据分布中的一列来估计复合索引的大小和效率

下一步-只有124个不同的设备并不意味着索引速度更快。相反-不太明显的值将树拆分为更小的部分,因此部分更大。例如,一百万个bigint值上的bigserial有一百万个不同的值,并且准确的id很快就得到了。而布尔列索引扫描只有两(三)个值,因此需要更长的时间

最后一个参数-两秒钟确实很慢。但是考虑到你扫描了600万行,比较时间戳,我认为2秒是可以接受的

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

我最后做了这样一件事:

SELECT DISTINCT d.id, c.timestamp, c.id, c.working
FROM devices d
INNER JOIN call_logs c on d.id = c.device_id AND c.timestamp = (SELECT max(t.timestamp) FROM call_logs t WHERE t.device_id = d.id)
结果好多了

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)

如果删除
不同的
,执行时间如何?如果你只想打最后一个电话,你不能添加
LIMIT 1
并使
DISTINCT
变得不必要吗?尽量避免DISTINCT,请参见:但是LIMIT 1只给我1个设备,每个设备需要1个