【问题标题】:How do I reduce the cost of set_bit in Postgres?如何降低 Postgres 中 set_bit 的成本?
【发布时间】:2018-09-22 18:46:54
【问题描述】:

我正在运行 PostgreSQL 9.6,并且正在对以下表结构进行实验:

CREATE TABLE my_bit_varying_test (
  id SERIAL PRIMARY KEY,
  mr_bit_varying BIT VARYING
);

为了了解如果我同时在 100,000 位数据上重置位,我可以期待多少性能,我编写了一个小的 PL/pgSQL 块,如下所示:

DO $$
DECLARE
  t  BIT VARYING(100000) := B'0';
  idd INT;
BEGIN
  FOR I IN 1..100000
  LOOP
    IF I % 2 = 0 THEN
      t := t || B'1';
    ELSE
      t := t || B'0';
    end if;
  END LOOP ;

  INSERT INTO my_bit_varying_test (mr_bit_varying) VALUES (t) RETURNING id INTO idd;
  UPDATE my_bit_varying_test SET mr_bit_varying = set_bit(mr_bit_varying, 100, 1) WHERE id = idd;
  UPDATE my_bit_varying_test SET mr_bit_varying = set_bit(mr_bit_varying, 99, 1) WHERE id = idd;
  UPDATE my_bit_varying_test SET mr_bit_varying = set_bit(mr_bit_varying, 34587, 1) WHERE id = idd;
  UPDATE my_bit_varying_test SET mr_bit_varying = set_bit(mr_bit_varying, 1, 1) WHERE id = idd;

  FOR I IN 1..100000
  LOOP
    IF I % 2 = 0 THEN
      UPDATE my_bit_varying_test
      SET mr_bit_varying = set_bit(mr_bit_varying, I, 1)
      WHERE id = idd;
    ELSE
      UPDATE my_bit_varying_test
      SET mr_bit_varying = set_bit(mr_bit_varying, I, 0)
      WHERE id = idd;
    end if;
  END LOOP ;
END
$$;

但是,当我运行 PL/pgSQL 时,它需要几分钟才能完成,我已将其范围缩小到更新表的 for 循环。由于BIT VARYING 列上的压缩,它运行缓慢吗?有什么方法可以提高性能吗?

编辑 这是一个模拟的简化示例。这实际上是因为我有数以万计的作业正在运行,每个作业都需要报告它们的状态,每隔几秒钟更新一次。

现在,我可以对其进行规范化,并创建一个“运行状态”表来保存所有工作人员及其状态,但这将涉及存储数万行。所以,我的想法是我可以使用位图来存储客户端和状态,并且掩码会按照顺序告诉我哪些已运行,哪些已完成。前面的位将用作“错误位”,因为我不需要确切知道哪个客户端失败了,只知道存在失败。

例如,您可能有 5 名工人从事一份工作。如果它们都完成了,那么状态将为“01111”,表示所有作业都已完成,并且没有一个失败。如果 2 号工人失败,则状态为“111110”,表示出现错误,除最后一名工人外,所有工人都已完成。

因此,您可以将其视为处理大量作业状态的一种人为方式。当然我有其他想法,但即使我走那条路,对于未来,我仍然想知道如何快速更新变量,因为我很好奇。

【问题讨论】:

  • 循环更新在性能方面几乎从来都不是一个好主意。我也不明白你为什么一遍又一遍地更新同一行
  • 这是一个模拟的最小示例。生产中实际发生的情况是,将有数千个线程同时更新该可变位行。如果完成这样一个简单的循环需要几分钟,那么我什至不想考虑它对数千个线程的作用。
  • 一个简单循环可能会杀死任何软件。您应该知道循环创建了 100000 行并删除了其中的 99999。你是说你将同时拥有 100000 个客户吗?
  • 我是说我可能有 10 或 15 个客户,但他们需要更新该行数千次。为了完成这项工作,每个客户端的性能必须比我看到的更新性能好得多。
  • 让我编辑问题,看看我是否可以提供更多上下文。

标签: postgresql sql-update plpgsql


【解决方案1】:

如果您的问题确实是 TOAST 压缩,您可以简单地为该表禁用它:

ALTER TABLE my_bit_varying_test SET STORAGE EXTERNAL;

【讨论】:

    【解决方案2】:

    您可以尝试使用基于集合的方法来替换第二个循环。基于集合的方法通常比循环更胖。使用generate_series() 获取索引。

    UPDATE my_bit_varying_test
           SET mr_bit_varying = set_bit(mr_bit_varying, gs.i, abs(gs.i % 2 - 1))
           FROM generate_series(1, 100000) gs(i)
           WHERE id = idd;
    

    如果您还没有索引,也可以考虑在 my_bit_varying_test (id) 上创建索引。

    【讨论】:

    • my_bit_varying_test 上的索引是否真的有助于更新性能?请参阅上面的评论线程。会有很多线程试图更新同一行。
    • @Brad:除非表很小,否则索引可能有助于检索需要更快更新的行,是的。当然,如果有帮助,您需要亲自尝试一下。
    猜你喜欢
    • 2022-01-16
    • 2022-01-14
    • 2022-01-15
    • 1970-01-01
    • 1970-01-01
    • 2019-08-15
    • 2020-10-01
    • 2022-08-22
    • 2022-01-14
    相关资源
    最近更新 更多