使用 PostgreSQL 12,我要求每晚需要“刷新”一些数据。这意味着:数据库必须根据当前日期进行一些计算并将结果写入表中。这通常有效。然而,它的工作方式似乎并不完美。
基本上,有一个这样的 PLV8 查询:
做$$
var myData = plv8.execute('SELECT id FROM my_table');
for(let i = 0; < myData.length; i++) {
plv8.execute('SELECT my_function_which_refreshes_lots_of_data($1)',[myData[i].id);
}
$$ LANGUAGE plv8;
my_function_which_refreshes_lots_of_data
获取一项的 id 并完成所有工作。必须这样做,因为有时单行也需要触发刷新。
问题是不知何故这已经失控了。目前,查询运行了一个多小时,占用了 99% 的内存,填满了交换空间,并且大部分时间处于状态“D”-“磁盘睡眠(不间断)”。查询的持续时间不是问题,因为它通常在夜间运行,由 cronjob 触发。但是,内存消耗和进程状态(基本上就像僵尸一样)是或可能是一个问题。
所以,我的想法是:据我所知,PostgreSQL 将所有对表的更改保存在内存中,同时查询运行并仅在最后写入它们。是否可以手动触发“将您到目前为止所拥有的内容写入磁盘并释放内存”?
之后我会在我的代码中这样做plv8.execute('SELECT my_function_which_refreshes_lots_of_data($1)',[myData[i].id);
。当然,如果进程崩溃或发生其他情况,这将导致状态不一致:部分项目将由 处理my_function_which_refreshes_lots_of_data
,而其余项目将保持原样。但是,这总比不写任何项目要好。并且绝对比运行这么长时间并消耗所有内存的查询要好。
我知道我可以以某种方式尝试在客户端拆分查询。但是,由于某些原因,这对我来说不太方便。
不,PostgreSQL 不会将正在进行的事务缓存在内存中。它们像往常一样写入磁盘。
提交时发生的所有事情都是预写日志(WAL)和提交日志被刷新到磁盘。
因此,您必须进一步调查以找出导致 I/O 过载的原因。
有用的调查路线是:
使用
pg_stat_statements
withpg_stat_statements.track = all
查找导致负载的 SQL 语句。设置
track_io_timing = on
以查看哪些查询对 I/O 负载的贡献最大。检查每个函数调用是否在单独的事务中运行。
如果是,请在单个事务中捆绑多个调用 - 也许许多 WAL 刷新正在杀死您。