是否有一种特别有效的方法来批量更新Postgres中的大型或记录的简单布尔字段?
我有一个包含数百万行的表,有时我想在这些行的大型但索引良好的子集上标记fresh=false
。
但是,如果我尝试做显而易见的事情:
UPDATE mytable SET fresh=false WHERE mycriteria;
它运行了几个小时,消耗所有内存,开始交换,使我的机器几乎无法使用,迫使我终止进程,导致数据库中的任何数据都没有变化。
相反,我已经编写了一个bash脚本来一次运行几千条记录的迷你块中的这个更新,这仍然需要几个小时,但至少可以完成工作,并为我提供进度信息。还有更好的方法吗?
答案 0 :(得分:2)
它运行了几个小时,消耗所有内存,开始交换,使我的机器几乎无法使用,迫使我终止进程,导致数据库中的任何数据都没有变化。
根据您可能已定义AFTER UPDATE ... FOR EACH ROW
个触发器的说明。
目前,PostgreSQL(在9.4和之前的情况下为真)至少使用内存中队列作为触发器。它是一个高效的队列,但它仍然在内存中,并且在几百万行开始真正加起来之后。
要确认是这种情况,您应该gdb
将postgres
进程附加到gdb -p the-big-postgres-process-id
进程,并使用gdb -p 1234
进行工作。 1234
postgres
top
是SELECT pg_backend_pid()
的pid,显示为使用UPDATE
中的大量内存。或者您可以gdb
运行(gdb)
。
无论哪种方式,一旦你有(gdb) p MemoryContextStats(TopMemoryContext)
(gdb) detach
(gdb) quit
附加,并且你在gdb
提示符运行:
AFTER UPDATE ... FOR EACH ROW
如果FOR EACH STATEMENT
抱怨缺少符号,则可能必须先安装debuginfo包;见the instructions on the wiki。
这将确认内存的实际位置。
如果这确实是NEW
触发器,那么您的选项是:
请改用OLD
触发器。在这种情况下,无法获得BEFORE
和AFTER
。
使用{{1}}触发器;或
赞助开发{{1}}触发器队列的溢出到磁盘存储; - )