内容提要
PostgreSQL 17 引入 GROUP BY 重排序优化,可自动调整多列分组键顺序,使其与 ORDER BY 或底层索引顺序一致,避免额外排序并改用更高效的索引扫描。测试中,原本因分组顺序不当而全表扫描并排序的查询,启用后性能显著提升。该功能由 GUC 参数 enable_group_by_reordering 控制,可随时关闭。
延伸解读
GROUP BY 列顺序为何影响性能
文章通过对比实验说明,当 GROUP BY 的列顺序与索引或 ORDER BY 顺序不一致时,PostgreSQL 可能放弃索引扫描,转而执行全表扫描加排序。例如,对 (c1,c2) 索引,GROUP BY c1,c2 可走 Index Only Scan,而 GROUP BY c2,c1 则触发 Seq Scan 和 HashAggregate,读取数据量相差约 5 倍。这提醒我们,分组键顺序并非无关紧要,尤其在机械硬盘或云盘上,响应时间可能急剧恶化。
新优化如何自动调整分组顺序
PostgreSQL 17 引入的 GROUP BY 重排序功能,会在优化阶段尝试多种分组键顺序,寻找代价更低的方案。它不会改变查询语义,因为分组本身不要求顺序。优化器会优先考虑与 ORDER BY 或底层索引顺序一致的分组顺序,从而避免额外排序,甚至直接使用索引扫描。文章中的测试显示,启用后,原本因顺序不当而全表扫描的查询重新用上了高效的 Index Only Scan。
enable_group_by_reordering 参数的作用与风险
该功能由 GUC 参数 enable_group_by_reordering 控制,默认开启,可随时关闭。文章指出,优化器会保留用户显式指定的分组顺序,假设用户可能有额外考量。但引入重排序可能增加规划器的搜索开销。社区在讨论中强调,不希望因此显著增加规划复杂度。因此,在升级到 PostgreSQL 17 后,建议关注该参数对规划时间和执行计划的影响,必要时可关闭以回退到原有行为。
对 ORM 与遗留查询的实用价值
文章提到,手动调整 GROUP BY 列顺序是简单优化,但使用 ORM 时,开发者可能失去对生成 SQL 的控制,难以确保顺序最优。GROUP BY 重排序能自动纠正这类问题,减少因 ORM 生成顺序不当导致的性能衰退。对于无法轻易修改查询的应用,这一特性提供了透明改进。不过,它并非万能:若查询本身存在其他瓶颈,或规划器无法找到更优顺序,性能提升可能有限。
Q&A
PostgreSQL 17 的 GROUP BY 重排序是什么?
GROUP BY 重排序是 PostgreSQL 17 引入的一项优化功能,它可以在优化器阶段探索多列 GROUP BY 子句的不同列顺序,尝试找到更高效的排序方式,从而避免额外的排序操作或使用更高效的索引扫描。
GROUP BY 重排序如何提升查询性能?
当 GROUP BY 子句的列顺序与 ORDER BY 或底层索引顺序不一致时,原本可能导致全表扫描和额外排序。启用重排序后,优化器会自动调整 GROUP BY 的列顺序,使其与 ORDER BY 或索引顺序匹配,从而避免排序并利用索引扫描,显著减少数据读取量和执行时间。
如何启用或禁用 GROUP BY 重排序?
可以通过 GUC 参数 enable_group_by_reordering 控制。设置为 on 启用,设置为 off 禁用。默认情况下该参数为 on。
GROUP BY 重排序在什么场景下特别有用?
当查询中的 GROUP BY 子句列顺序未优化,例如由 ORM 生成或手动编写时顺序不当,导致无法利用索引或需要额外排序时,该功能可以自动调整顺序,避免性能问题。
GROUP BY 重排序会改变查询结果吗?
不会。分组操作本身不要求任何特定顺序,因此调整 GROUP BY 的列顺序不会改变分组结果,只是可能影响执行计划的效率。
GROUP BY 重排序功能是如何开发出来的?
该功能由 Andrei Lepikhov 和 Teodor Sigaev 开发,经过多位审阅者(包括 Tomas Vondra、Robert Haas、Tom Lane 等)的评审,从 2018 年开始,历时五年半才最终提交。