如何在PL/pgSQL中释放内存,避免UDF运行时出现OOM问题
PostgreSQL 存储过程循环执行OOM问题解决方案
问题原因
PL/pgSQL存储过程的内存上下文默认绑定到过程的整个生命周期,过程内执行的普通SQL查询的执行计划会被缓存,每次查询产生的临时内存碎片也不会在循环间隙主动回收,循环执行上千次后累计的内存占用就会触发OOM。
可落地的解决方法
- 方案1:用
EXECUTE动态执行查询,避免执行计划缓存
把循环内的select count(*) from data_table where something into cnt;改为动态执行的写法,动态查询的执行计划不会被持久缓存,执行完成后相关内存会立刻回收:-- 直接拼接条件的写法 EXECUTE 'select count(*) from data_table where 你的查询条件' INTO cnt; -- 如果查询条件带参数,推荐用USING传参,避免SQL注入,示例: -- EXECUTE 'select count(*) from data_table where ts between $1 and $2 AND box @> $3' INTO cnt USING temprow.ts, temprow.te, temprow.box; - 方案2:循环内主动释放缓存资源
每次循环执行完查询、提交事务后,主动丢弃缓存的执行计划,回收对应内存,修改后的循环逻辑如下:FOR temprow IN SELECT * FROM query_ranges LOOP select count(*) from data_table where something into cnt; sum = sum + cnt; COMMIT; DISCARD PLANS; -- 新增这行,丢弃所有会话级缓存的执行计划 END LOOP; - 方案3:把循环逻辑移到存储过程外部
存储过程执行完成后,其绑定的所有内存资源都会被完全释放,你可以把单次查询的逻辑封装成独立函数,用外部脚本(比如shell的psql循环、Python脚本)逐次调用,完全避免过程内循环的内存累计问题,这是最稳定的解决方案。 - 方案4:调整参数限制单次查询内存占用
适当调小work_mem参数的会话级配置,避免单次查询分配过大的内存,在过程开头加上对应配置即可:SET work_mem = '64MB'; -- 可根据你的实际情况调整大小,不要超过系统可用内存的1/100
内容的提问来源于stack exchange,提问作者Bobi.Liu
相关产品推荐
相关产品推荐

