我试图了解Postgres(v10.9)上一个奇怪的性能问题。
我们有一个requests
表,我想在几个时间范围内获取一组特定用户的所有请求。以下是相关信息:
user_id
列。而是有一个名为params
的jsonb列,其中user_id
字段存储为字符串。time
类型为timestamptz的列,并使用标准BTREE索引对其进行了索引。params->>'user_id'
上还有一个单独的BTREE索引。我正在运行的查询基于以下模板:
SELECT *
FROM requests
WHERE params->>'user_id' = ANY (VALUES ('id1'), ('id2'), ('id3')...)
AND time > 't1' AND time < 't2'
此处的ID和时间是实际ID和时间的占位符。
我正在连续两个时间范围(每个2周)内运行这样的查询。前几个时间范围的查询每个都需要花费几分钟,这在生产方面显然很长,但对于研究目的来说可以。然后突然之间查询运行时出现了急剧的高峰,并且每次启动都耗时数小时,即使对于脱机目的,也变得站不住脚了。
此峰值每次都在相同范围内发生。值得注意的是,在此时间范围内,请求总数增加了x1.5。当然,与之前的时间范围相比,它的数量更多,但不足以保证整个数量级的峰值。
以下是在合理的运行时间下最后一个时间范围内EXPLAIN ANALYZE
的输出:
Hash Join (cost=442.69..446645.35 rows=986171 width=1217) (actual time=66.305..203593.238 rows=445175 loops=1)
Hash Cond: ((requests.params ->> 'user_id'::text) = \"*VALUES*\".column1)
-> Index Scan using requests_time_idx on requests (cost=0.56..428686.19 rows=1976888 width=1217) (actual time=14.336..201643.439 rows=2139604 loops=1)
Index Cond: ((\"time\" > '2019-02-12 22:00:00+00'::timestamp with time zone) AND (\"time\" < '2019-02-26 22:00:00+00'::timestamp with time zone))
-> Hash (cost=439.62..439.62 rows=200 width=32) (actual time=43.818..43.818 rows=29175 loops=1)
Buckets: 32768 (originally 1024) Batches: 1 (originally 1) Memory Usage: 2536kB
-> HashAggregate (cost=437.62..439.62 rows=200 width=32) (actual time=24.887..33.775 rows=29175 loops=1)
Group Key: \"*VALUES*\".column1
-> Values Scan on \"*VALUES*\" (cost=0.00..364.69 rows=29175 width=32) (actual time=0.006..10.303 rows=29175 loops=1)
Planning time: 133.807 ms
Execution time: 203697.360 ms
如果我正确理解这一点,似乎大部分时间都用于按时间范围过滤请求,即使:
感谢您对如何解决此问题的任何想法,谢谢!
答案 0 :(得分:0)
由于要检索的行太多,因此查询永远不会很快。
不幸的是,没有一个索引可以同时满足这两个条件,但是您可以使用以下两个条件:
CREATE INDEX ON requests ((params->>'user_id'));
CREATE INDEX ON requests (time);
然后,您可以希望进行两次位图索引扫描,并通过“位图或”进行连接。
我不确定这是否会提高性能; PostgreSQL仍然可以选择当前的计划,这不是一个坏主意。如果缓存了索引或快速访问了存储,请相应地设置effective_cache_size
或random_page_cost
,这将使PostgreSQL倾向于索引扫描。