PostgreSQL 聚合查询很慢
PostgreSQL aggregate query is very slow
我有一个 table,其中包含一个 timestamp
列和一个 source
列 varchar(20)
。我每小时向此 table 中插入几千个条目,我想显示此数据的汇总。我的查询如下所示:
EXPLAIN (analyze, buffers) SELECT
count(*) AS count
FROM frontend_car c
WHERE date_created at time zone 'cet' > now() at time zone 'cet' - interval '1 week'
GROUP BY source, date_trunc('hour', c.date_created at time zone 'CET')
ORDER BY source ASC, date_trunc('hour', c.date_created at time zone 'CET') DESC
我已经创建了一个这样的索引:
create index source_date_created on
table_name(
(date_created AT TIME ZONE 'CET') DESC,
source ASC,
date_trunc('hour', date_created at time zone 'CET') DESC
);
我的 ANALYZE
的输出是:
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------------------------------
Sort (cost=142888.08..142889.32 rows=495 width=16) (actual time=10242.141..10242.188 rows=494 loops=1)
Sort Key: source, (date_trunc('hour'::text, timezone('CET'::text, date_created)))
Sort Method: quicksort Memory: 63kB
Buffers: shared hit=27575 read=28482
-> HashAggregate (cost=142858.50..142865.93 rows=495 width=16) (actual time=10236.393..10236.516 rows=494 loops=1)
Group Key: source, date_trunc('hour'::text, timezone('CET'::text, date_created))
Buffers: shared hit=27575 read=28482
-> Bitmap Heap Scan on frontend_car c (cost=7654.61..141002.20 rows=247507 width=16) (actual time=427.894..10122.438 rows=249056 loops=1)
Recheck Cond: (timezone('cet'::text, date_created) > (timezone('cet'::text, now()) - '7 days'::interval))
Rows Removed by Index Recheck: 141143
Heap Blocks: exact=27878 lossy=26713
Buffers: shared hit=27575 read=28482
-> Bitmap Index Scan on frontend_car_source_date_created (cost=0.00..7592.74 rows=247507 width=0) (actual time=420.415..420.415 rows=249056 loops=1)
Index Cond: (timezone('cet'::text, date_created) > (timezone('cet'::text, now()) - '7 days'::interval))
Buffers: shared hit=3 read=1463
Planning time: 2.430 ms
Execution time: 10242.379 ms
(17 rows)
显然这太慢了,在我看来它应该只能使用索引来计算,如果我只使用时间或源进行聚合,它相当快,但不知何故加在一起它很慢。
这是在一个相当小的 VPS 上,只有 512MB 的内存,数据库目前包含大约 700k 行。
从我的阅读来看,大部分时间似乎花在了重新检查上,这意味着索引不适合内存?
听起来您真正需要的是一个单独的聚合 table,它通过详细 table 中的触发器插入或更新记录。摘要 table 将包含您的源列、一个仅包含日期和小时部分(截断任何分钟)的 date/time 字段,最后是 运行 计数。
随着记录的插入,此摘要 table 得到更新,然后您的查询可以直接在此 table 上进行。由于它已经按来源、日期和小时预先聚合,您的查询只需要应用 where 子句并按来源排序。
我对 postgresql 一点都不流利,但我确信他们有自己的插入触发器方法。因此,如果您每小时有 1000 个条目,并假设您有 10 个来源。此汇总摘要的整个结果集 table 仅为 24(小时)* ex 50(来源)= 每天 1200 条记录,而每天 50k、60k、70k+。如果您随后需要每个给定 date/hour 基础的确切详细信息,则可以根据需要深入了解详细信息。但实际上,您不清楚有多少 "sources"。
我强烈认为这是满足您需求的解决方案。
我有一个 table,其中包含一个 timestamp
列和一个 source
列 varchar(20)
。我每小时向此 table 中插入几千个条目,我想显示此数据的汇总。我的查询如下所示:
EXPLAIN (analyze, buffers) SELECT
count(*) AS count
FROM frontend_car c
WHERE date_created at time zone 'cet' > now() at time zone 'cet' - interval '1 week'
GROUP BY source, date_trunc('hour', c.date_created at time zone 'CET')
ORDER BY source ASC, date_trunc('hour', c.date_created at time zone 'CET') DESC
我已经创建了一个这样的索引:
create index source_date_created on
table_name(
(date_created AT TIME ZONE 'CET') DESC,
source ASC,
date_trunc('hour', date_created at time zone 'CET') DESC
);
我的 ANALYZE
的输出是:
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------------------------------
Sort (cost=142888.08..142889.32 rows=495 width=16) (actual time=10242.141..10242.188 rows=494 loops=1)
Sort Key: source, (date_trunc('hour'::text, timezone('CET'::text, date_created)))
Sort Method: quicksort Memory: 63kB
Buffers: shared hit=27575 read=28482
-> HashAggregate (cost=142858.50..142865.93 rows=495 width=16) (actual time=10236.393..10236.516 rows=494 loops=1)
Group Key: source, date_trunc('hour'::text, timezone('CET'::text, date_created))
Buffers: shared hit=27575 read=28482
-> Bitmap Heap Scan on frontend_car c (cost=7654.61..141002.20 rows=247507 width=16) (actual time=427.894..10122.438 rows=249056 loops=1)
Recheck Cond: (timezone('cet'::text, date_created) > (timezone('cet'::text, now()) - '7 days'::interval))
Rows Removed by Index Recheck: 141143
Heap Blocks: exact=27878 lossy=26713
Buffers: shared hit=27575 read=28482
-> Bitmap Index Scan on frontend_car_source_date_created (cost=0.00..7592.74 rows=247507 width=0) (actual time=420.415..420.415 rows=249056 loops=1)
Index Cond: (timezone('cet'::text, date_created) > (timezone('cet'::text, now()) - '7 days'::interval))
Buffers: shared hit=3 read=1463
Planning time: 2.430 ms
Execution time: 10242.379 ms
(17 rows)
显然这太慢了,在我看来它应该只能使用索引来计算,如果我只使用时间或源进行聚合,它相当快,但不知何故加在一起它很慢。
这是在一个相当小的 VPS 上,只有 512MB 的内存,数据库目前包含大约 700k 行。
从我的阅读来看,大部分时间似乎花在了重新检查上,这意味着索引不适合内存?
听起来您真正需要的是一个单独的聚合 table,它通过详细 table 中的触发器插入或更新记录。摘要 table 将包含您的源列、一个仅包含日期和小时部分(截断任何分钟)的 date/time 字段,最后是 运行 计数。
随着记录的插入,此摘要 table 得到更新,然后您的查询可以直接在此 table 上进行。由于它已经按来源、日期和小时预先聚合,您的查询只需要应用 where 子句并按来源排序。
我对 postgresql 一点都不流利,但我确信他们有自己的插入触发器方法。因此,如果您每小时有 1000 个条目,并假设您有 10 个来源。此汇总摘要的整个结果集 table 仅为 24(小时)* ex 50(来源)= 每天 1200 条记录,而每天 50k、60k、70k+。如果您随后需要每个给定 date/hour 基础的确切详细信息,则可以根据需要深入了解详细信息。但实际上,您不清楚有多少 "sources"。
我强烈认为这是满足您需求的解决方案。