我有一个表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。我不会想到这对索引来说会是一个缓慢的过程吗?出了什么问题?或者为什么这么慢?
答案 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个设备将花费微秒。