内容提要
本文介绍如何构建Postgres扩展querymem,用于预测查询内存消耗。通过解析查询、生成执行计划并遍历节点,累加work_mem和hash_mem_multiplier估算峰值内存。扩展支持GUC配置,可自动记录或阻止超限查询。文章强调从源码学习Postgres内部机制,并演示了实际测试结果。
延伸解读
为什么需要估算查询内存
Postgres 的 work_mem 是按操作分配的,而不是按查询。一个查询中如果有多个排序、哈希等节点,每个节点都会获得独立的 work_mem 分配,因此实际内存消耗可能远超预期。本文通过构建扩展来估算最坏情况下的内存使用,帮助用户理解查询的内存需求,并为设置 work_mem 提供参考。
从源码学习 Postgres 内部机制
构建该扩展需要深入 Postgres 源码,使用 raw_parser、transformTopLevelStmt、planner 等内部函数。这些函数在用户手册中没有详细示例,但源码中的 README 文件和头文件注释提供了关键线索。通过阅读源码,可以理解查询解析、分析和计划生成的过程,这是开发复杂扩展的基础。
扩展的实际应用与限制
querymem 扩展可以估算查询内存,并可通过 GUC 配置自动记录或阻止超限查询。但当前实现存在一些限制,例如未考虑并行计划,且对某些节点类型的计数可能不准确(如文中提到的额外节点导致估算偏差)。此外,直接调用 planner 可能存在内存泄漏风险,需要进一步优化。
Q&A
如何构建一个Postgres扩展来估算查询内存使用量?
构建一个名为querymem的Postgres扩展,通过解析查询、生成执行计划并遍历节点,累加work_mem和hash_mem_multiplier来估算峰值内存。扩展支持GUC配置,可自动记录或阻止超限查询。
querymem扩展是如何估算查询内存的?
querymem通过遍历执行计划中的每个节点,为普通节点累加1.0,为哈希节点累加hash_mem_multiplier(默认2.0),然后将总和乘以work_mem得到最坏情况下的内存估计。
querymem扩展需要哪些开发环境?
需要Postgres 18源码或开发头文件,以及构建工具链。推荐使用基于postgres:18的Docker镜像,包含所有必要的依赖,如build-essential、clang、postgresql-server-dev-18等。
querymem扩展如何自动记录或阻止超限查询?
通过定义GUC参数querymem.log_size和querymem.max_query_size,并设置ExecutorStart_hook,在每次执行前估算内存,超过log_size则记录日志,超过max_query_size则报错阻止执行。
querymem扩展的测试结果如何?
测试显示,简单顺序扫描估计为4MB,带排序的查询为8MB,带哈希连接的查询为28MB,带物化CTE的查询为57MB,与理论计算一致。
构建querymem扩展时,如何从源码中学习Postgres内部机制?
文章强调,当文档不足时,应阅读源码中的README文件、头文件注释和contrib目录中的示例,例如parser.c、optimizer/README等,以理解解析、规划和节点遍历等机制。