【问题标题】:Update VERY LARGE PostgreSQL database table efficiently高效更新 VERY LARGE PostgreSQL 数据库表
【发布时间】:2010-09-11 20:27:58
【问题描述】:

我在 PostgresQL 中有一个非常大的数据库表和一个类似“已复制”的列。每一个新行开始时都是未复制的,稍后将被后台程序复制到另一个东西上。该表“btree(ID) WHERE replicated=0”上有一个部分索引。后台程序最多选择 2000 个条目(LIMIT 2000),对它们进行处理,然后使用 2000 个准备好的 sql 命令在一个事务中提交更改。

现在的问题是我想给用户一个选项来重置这个复制值,让它再次全部为零。

一个更新表集replicated=0;

不可能:

  • 需要很长时间
  • 由于 MVCC,它复制了表格的大小
  • 在一个事务中完成:要么失败,要么通过。

在这种情况下,我实际上不需要事务功能:如果系统出现故障,它将只处理其中的一部分。

其他几个问题: 做一个

update set replicated=0 where id >10000 and id<20000

也很糟糕:它对整个表进行顺序扫描,这太慢了。 如果不这样做,它仍然会很慢,因为它会进行太多的搜索。

我真正需要的是一种遍历所有行、更改它们而不被绑定到巨大事务的方法。

奇怪的是

UPDATE table 
  SET replicated=0 
WHERE ID in (SELECT id from table WHERE replicated= LIMIT 10000)

也很慢,虽然这应该是件好事:按 DISK-order 遍历表...

(请注意,在这种情况下,还有一个索引涵盖了这一点)

(Mysql 之类的更新 LIMIT 不适用于 PostgresQL)

顺便说一句:真正的问题更复杂,我们在这里讨论的是已经部署的嵌入式系统,因此远程架构更改很困难,但可能 不幸的是,它是 PostgresQL 7.4。

我所说的行数是例如90000000。数据库的大小可以是几十G。

数据库本身只包含5张表,其中一张非常大。 但这不是一个糟糕的设计,因为这些嵌入式盒子只与一种实体一起操作,而不是 ERP 系统或类似的东西!

有什么想法吗?

【问题讨论】:

    标签: sql database postgresql sql-update mvcc


    【解决方案1】:

    如何添加一个新表来存储此复制值(以及将每条记录链接到主表的主键)。然后,您只需为每个复制项目添加一条记录,并删除记录以删除复制标志。 (或者可能反过来 - 每个非复制记录的记录,取决于常见情况)。

    当您想将它们全部设置回 0 时,这也将简化情况,因为您可以截断表(这会将磁盘上的表大小归零,您甚至不必清理以释放空间)

    【讨论】:

    • 这是一个非常好的主意,尽管不幸的是它需要更改架构(一个冗长的更新过程)。我真正喜欢这种方法的地方在于,实际上当前的部分索引在内部与这个想法非常相似!只有更加灵活和易于管理。
    【解决方案2】:

    如果您尝试重置整个表,而不仅仅是几行,通常更快(在非常大的数据集上——而不是在常规表上)简单地CREATE TABLE bar AS SELECT everything, but, copied, 0 FROM foo,然后交换表并删除旧的一。显然,您需要确保在执行此操作时没有将任何内容插入原始表中。您还需要重新创建该索引。

    编辑:一个简单的改进,以避免在复制 14 GB 时锁定表:

    lock ;
    create a new table, bar;
    swap tables so that all writes go to bar;
    unlock;
    create table baz as select from foo;
    drop foo;
    create the index on baz;
    lock;
    insert into baz from bar;
    swap tables;
    unlock;
    drop bar;
    

    (在您进行复制时让写入发生,并在事后插入它们)。

    【讨论】:

      【解决方案3】:

      虽然您不可能解决空间使用问题(它是暂时的,直到真空),但您可能真的可以在时钟时间方面加快进程。 PostgreSQL 使用 MVCC 的事实意味着您应该能够做到这一点,而不会出现与新插入的行相关的任何问题。 create table as select 将解决一些性能问题,但不允许继续使用该表,并且占用同样多的空间。只需放弃索引,重新构建它,然后做一个真空。

      drop index replication_flag;
      update big_table set replicated=0;
      create index replication_flag on big_table btree(ID) WHERE replicated=0;
      vacuum full analyze big_table;
      

      【讨论】:

        【解决方案4】:

        这是伪代码。您将需要 400MB(对于 int)或 800MB(对于 bigints)临时文件(如果有问题,您可以使用 zlib 对其进行压缩)。它需要对一张桌子进行大约 100 次扫描以进行真空吸尘器。但它不会使表膨胀超过 1%(任何时候最多 1000000 个死行)。您还可以用更少的扫描来换取更多的表膨胀。

        // write all ids to temporary file in disk order                
        // no where clause will ensure disk order
        $file = tmpfile();
        for $id, $replicated in query("select id, replicated from table") {
                if ( $replicated<>0 ) {
                        write($file,&$id,sizeof($id));
                }
        }
        
        // prepare an update query
        query("prepare set_replicated_0(bigint) as
                update table set replicated=0 where id=?");
        
        // reread this file, launch prepared query and every 1000000 updates commit
        // and vacuum a table
        rewind($file);
        $counter = 0;
        query("start transaction");
        while read($file,&$id,sizeof($id)) {
                query("execute set_replicated_0($id)");
                $counter++;
                if ( $counter % 1000000 == 0 ) {
                        query("commit");
                        query("vacuum table");
                        query("start transaction");
                }
        }
        query("commit");
        query("vacuum table");
        close($file);
        

        【讨论】:

          【解决方案5】:

          我猜你需要做的是 一种。将 2000 条记录的 PK 值复制到具有相同标准限制的临时表中,等等。 湾。选择相同的 2000 条记录并按原样在游标中执行必要的操作。 C。如果成功,请针对临时表中的记录运行单个更新查询。清除临时表并再次运行步骤 a。 d。如果不成功,请清除临时表而不运行更新查询。 简单、高效、可靠。 问候, 韩特

          【讨论】:

            【解决方案6】:

            我认为最好将您的 postgres 更改为 8.X 版本。可能原因是 Postgres 的低版本。也试试下面的这个查询。我希望这会有所帮助。

            UPDATE table1 SET name = table2.value
            FROM table2 
            WHERE table1.id = table2.id;
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2010-09-07
              • 2021-10-11
              • 2010-09-21
              • 2011-01-18
              • 2016-03-02
              • 1970-01-01
              相关资源
              最近更新 更多