为啥 postgresql 不使用我的 group by 聚合索引?

Posted

技术标签:

【中文标题】为啥 postgresql 不使用我的 group by 聚合索引?【英文标题】:Why isn't postgresql using an index with my group by aggregate?为什么 postgresql 不使用我的 group by 聚合索引? 【发布时间】:2022-01-15 21:42:24 【问题描述】:

我在 postgresql 数据库中有一个启用了 timescaledb 扩展的表,如下所示:

+------------+--------------------------+-------------+
| Column     | Type                     | Modifiers   |
|------------+--------------------------+-------------|
| time       | timestamp with time zone |  not null   |
| value      | double precision         |  not null   |
| being      | metric_being             |  not null   |
| device     | integer                  |  not null   |
+------------+--------------------------+-------------+

还有一个索引在桌子上:

"metrics_device_time_idx" btree (device, "time" DESC)

但是当我使用 Group By 查询表时:

explain select max(time), device from metrics group by device;

它不使用索引:

+----------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+
| QUERY PLAN                                                                                                        |
|-------------------------------------------------------------------------------------------------------------------|
| Finalize GroupAggregate  (cost=104577.41..104588.61 rows=22 width=12)                                             |
|   Group Key: _hyper_9_95_chunk.device                                                                             |
|   ->  Gather Merge  (cost=104577.41..104587.95 rows=88 width=12)                                                  |
|         Workers Planned: 4                                                                                        |
|         ->  Sort  (cost=103577.35..103577.41 rows=22 width=12)                                                    |
|               Sort Key: _hyper_9_95_chunk.device                                                                  |
|               ->  Partial HashAggregate  (cost=103576.64..103576.86 rows=22 width=12)                             |
|                     Group Key: _hyper_9_95_chunk.device                                                           |
|                     ->  Parallel Append  (cost=0.00..95035.06 rows=1708317 width=12)                              |
|                           ->  Parallel Seq Scan on _hyper_9_95_chunk  (cost=0.00..44602.70 rows=1122370 width=12) |
|                           ->  Parallel Seq Scan on _hyper_9_92_chunk  (cost=0.00..24807.61 rows=756061 width=12)  |
+-------------------------------------------------------------------------------------------------------------------+

最后开始有点慢。另一方面,真正快 10 倍的是

select max(time), 29 from metrics where device = 29
union
select max(time), 30 from metrics where device = 30
union
...

为什么会这样?我可以使用group by 更改我的索引或查询以加快查询速度吗?为什么union 这么快?

【问题讨论】:

你是如何在 timescaledb 中对表进行分区的? 没有空间分区,超表创建时使用:select create_hypertable ('metrics', 'time'); 那么,一个设备的时间是跨多个时间序列交错的?对于您的查询,这似乎不是最佳选择。 【参考方案1】:

正如@Pavel Stehule 在他的回答中提到的,Postgres 没有实现索引跳过扫描,这是优化这些类型的查询所必需的。 Timescaledb 认识到这些类型的查询在时间序列分析中确实很有帮助,因此他们自己实现了索引跳过扫描。它存在于他们从 2.2.1 版本开始的扩展中,请参阅他们关于它的博客文章 here。

将扩展升级到 >= 2.2.1 后,可以重写查询以使用索引跳过扫描:

select distinct on (device) device, time from metrics order by device, time desc

然后使用他们的索引跳过扫描实现,在我的例子中,查询速度提高了大约 100 倍。

【讨论】:

【参考方案2】:

Postgres 不能在这种情况下使用索引。优化器现在不支持这一点。你可以找到一些关于这个的信息 - 有一个名为“索引跳过扫描”的补丁,但这项工作还没有完成。你可以使用一些workarounds。

【讨论】:

有没有办法使用 CTE 复制 group by,如链接中所述?我试图得到一个包含两列的结果集,timedevice 当然是。但我想念你使用时间刻度 - 它具有增强的优化器,所以也许会有其他更好的解决方案。 Timescaledb 和类似系统更敏感,您应该更仔细地遵循文档和模式。

以上是关于为啥 postgresql 不使用我的 group by 聚合索引?的主要内容,如果未能解决你的问题,请参考以下文章

带有注释的Django查询集,为啥将GROUP BY应用于所有字段?

为啥我的 PostgreSQL 数组索引没有被使用(Rails 4)?

PostgreSQL GROUP BY LOWER() 不工作

为啥 PostgreSQL 不使用三元索引

为啥 re.groups() 不为我的一个正确匹配的组提供任何东西?

为啥 Postgresql 使用过滤器而不是索引?