通过批量迭代表来更新比 PostgreSQL 中的整个表更快

dav*_*ley 12 postgresql performance update

我有一个大约有一百万行的表。

它正在生产中使用,我运行了一个UPDATE覆盖了大约 95% 的行。

之后5小时我取消了请求,因为它正在采取这么长时间。


该表有一个自动递增的 ID 列,因此我尝试将WHERE查询条件扩展为包含id BETWEEN 1 AND 500.

此更新在大约两秒钟内完成。然后我手动迭代了id500 个批次,例如BETWEEN 500 AND 1000, then BETWEEN 1000 AND 1500

按照这个速度,更新整个表需要 2000 个批次,每批次 500 个。

每 2 秒更新 2000 个批次只需一个多小时


我的问题是:

  1. 造成这种差异的原因是什么?
  2. 我不关心事务隔离,所以有没有办法模拟这种“批量更新”,而不必编写 2000 个更新的脚本来单独运行。

Chr*_*ris 10

我会尽量简短地回答您的问题,但由于我不太了解您对 PostgreSQL 的舒适程度,而且无论如何我都没有太多时间进行深入的解释,我' 将保持答案简单,如果您想了解更多信息,可以要求澄清。

1)为什么批量处理速度更快?

由于PostgreSQL 的预写日志的结构、RAM共享缓冲区空间的数量以及尝试UPDATE在单个事务中执行整个事务,我的猜测是您根本没有足够的计算资源来有效地处理更新单笔交易中近百万条记录。

PostgreSQL 有一个构建良好的并发控制系统,本质上意味着它必须UPDATE在您的UPDATE操作期间保持您的前行的旧副本可用。这样,如果在您更新时另一个客户端尝试访问这些行、更新失败或取消更新,您就不会丢失旧信息。

如果执行足够大的UPDATE,PostgreSQL 会将页面加载到内存中并对其进行修改,但最终会耗尽内存以供使用,因此如果它希望能够加载更多页面,它必须立即将这些页面临时复制到磁盘并继续交易。

而不是能够在一段时间内分摊磁盘写入,您只是迫使您的数据库陷入瓶颈。

2) 编写更新脚本

您绝对可以通过在PL/pgSQL 中创建函数来编写更新脚本。关于 PL/pgSQL 有很多东西需要学习,包括很多我可能不知道的,但一般来说,你可以做这样的事情

CREATE OR REPLACE FUNCTION mini_batch_update()
  RETURNS void AS
$BODY$
DECLARE

id_val integer;

BEGIN

  FOR id_val IN 0..2000
  LOOP

  **UPDATE GOES HERE**
  WHERE id > (500 * id_val) AND id <= 500 * (id_val + 1);

  END LOOP;


END
$BODY$
LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)

我没有花很多时间以顶级形式制作这个批处理功能;我的意思是,为了简单起见,我只是硬编码了几个数值。在您的情况下,您可能希望获得更详细的信息并包括:1) 检查最大 id 值的内容,以便您适当地设置边界,以及 2) 即使我对 500 的批次进行了硬编码,您也可以轻松地做到这一点函数输入参数。

抱歉,我没有时间对此进行测试或确保它确实运行良好。祝你好运!