我有一个 node.js 程序调用 Postgres(Amazon RDS 微实例)函数,get_jobs
在事务中,每秒 18 次使用node-postgres
brianc 的包。
节点代码只是briinc 的基本客户端池示例的增强版,大致类似于...
var pg = require('pg');
var conString = "postgres://username:password@server/database";
function getJobs(cb) {
pg.connect(conString, function(err, client, done) {
if (err) return console.error('error fetching client from pool', err);
client.query("BEGIN;");
client.query('select * from get_jobs()', [], function(err, result) {
client.query("COMMIT;");
done(); //call `done()` to release the client back to the pool
if (err) console.error('error running query', err);
cb(err, result);
});
});
}
function poll() {
getJobs(function(jobs) {
// process the jobs
});
setTimeout(poll, 55);
}
poll(); // start polling
所以 Postgres 得到:
2016-04-20 12:04:33 UTC:172.31.9.180(38446):XXX@XXX:[5778]:LOG: statement: BEGIN;
2016-04-20 12:04:33 UTC:172.31.9.180(38446):XXX@XXX:[5778]:LOG: execute <unnamed>: select * from get_jobs();
2016-04-20 12:04:33 UTC:172.31.9.180(38446):XXX@XXX:[5778]:LOG: statement: COMMIT;
...每 55 毫秒重复一次。
get_jobs
是用临时表写的,像这样
CREATE OR REPLACE FUNCTION get_jobs (
) RETURNS TABLE (
...
) AS
$BODY$
DECLARE
_nowstamp bigint;
BEGIN
-- take the current unix server time in ms
_nowstamp := (select extract(epoch from now()) * 1000)::bigint;
-- 1. get the jobs that are due
CREATE TEMP TABLE jobs ON COMMIT DROP AS
select ...
from really_big_table_1
where job_time < _nowstamp;
-- 2. get other stuff attached to those jobs
CREATE TEMP TABLE jobs_extra ON COMMIT DROP AS
select ...
from really_big_table_2 r
inner join jobs j on r.id = j.some_id
ALTER TABLE jobs_extra ADD PRIMARY KEY (id);
-- 3. return the final result with a join to a third big table
RETURN query (
select je.id, ...
from jobs_extra je
left join really_big_table_3 r on je.id = r.id
group by je.id
);
END
$BODY$ LANGUAGE plpgsql VOLATILE;
我使用了临时表模式,因为我知道这jobs
将始终是从 中提取的一小部分行really_big_table_1
,希望这将比具有多个连接和多个 where 条件的单个查询更好地扩展。(我在 SQL Server 上使用这个效果很好,我现在不信任任何查询优化器,但请告诉我这是否是 Postgres 的错误方法!)
查询在小表上运行 8 毫秒(从节点测量),有足够的时间在下一个开始之前完成一个作业“轮询”。
问题:以这种速度进行大约 3 小时的轮询后,Postgres 服务器内存不足并崩溃。
我已经尝试过的...
如果我在没有临时表的情况下重新编写函数,Postgres 不会耗尽内存,但我经常使用临时表模式,所以这不是解决方案。
如果我停止节点程序(它会终止用于运行查询的 10 个连接),内存就会释放。仅仅让节点在轮询会话之间等待一分钟不会产生相同的效果,因此显然 Postgres 后端与池连接相关联的资源正在保留。
如果我运行一段
VACUUM
时间轮询正在进行,它对内存消耗没有影响,并且服务器继续死亡。降低轮询频率只会改变服务器死机之前的时间量。
DISCARD ALL;
在每个之后添加COMMIT;
都没有效果。在s 上显式调用
DROP TABLE jobs; DROP TABLE jobs_extra;
afterRETURN query ()
而不是s。服务器仍然崩溃。ON COMMIT DROP
CREATE TABLE
根据 CFrei 的建议,添加
pg.defaults.poolSize = 0
到节点代码中以尝试禁用池。服务器仍然崩溃,但与之前所有看起来像下面的第一个峰值的测试相比,花费的时间和交换时间要长得多(第二个峰值)。后来我发现pg.defaults.poolSize = 0
可能无法按预期禁用池。
基于此:“临时表无法通过 autovacuum 访问。因此,应通过会话 SQL 命令执行适当的清理和分析操作。”,我尝试从
VACUUM
节点服务器运行VACUUM
会话”命令)。我实际上无法让这个测试工作。我的数据库中有许多对象,并且VACUUM
对所有对象进行操作时,执行每个作业迭代所花费的时间太长。只限于VACUUM
临时表是不可能的 - (a) 你不能运行VACUUM
在事务中和(b)在事务之外,临时表不存在。:P 编辑:稍后在 Postgres IRC 论坛上,一个有用的小伙子解释说 VACUUM 与临时表本身无关,但对于清理从pg_attributes
该 TEMP TABLES 原因创建和删除的行很有用。无论如何,VACUUMing“在会话中”不是答案。DROP TABLE ... IF EXISTS
之前CREATE TABLE
,而不是ON COMMIT DROP
。服务器仍然死机。CREATE TEMP TABLE (...)
而insert into ... (select...)
不是CREATE TEMP TABLE ... AS
, 而不是ON COMMIT DROP
。服务器死机。
那么ON COMMIT DROP
不是释放所有相关资源吗?还有什么可以保存记忆?我该如何释放它?