Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/database/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
高效更新非常大的PostgreSQL数据库表_Sql_Database_Postgresql_Sql Update_Mvcc - Fatal编程技术网

高效更新非常大的PostgreSQL数据库表

高效更新非常大的PostgreSQL数据库表,sql,database,postgresql,sql-update,mvcc,Sql,Database,Postgresql,Sql Update,Mvcc,我在PostgresQL中有一个非常大的数据库表和一个类似复制的列。每一个新行开始时都是未复制的,稍后将通过后台程序复制到另一个对象。表btreeID上有一个部分索引,其中replicated=0。后台程序对最多2000个条目执行select LIMIT 2000,处理它们,然后使用2000个准备好的sql命令在一个事务中提交更改 现在的问题是,我想给用户一个选项来重置这个复制值,让它再次为零 已复制的更新表集=0 不可能: 这需要很多时间 由于MVCC,它复制了选项卡的大小 它在一个事务中完成

我在PostgresQL中有一个非常大的数据库表和一个类似复制的列。每一个新行开始时都是未复制的,稍后将通过后台程序复制到另一个对象。表btreeID上有一个部分索引,其中replicated=0。后台程序对最多2000个条目执行select LIMIT 2000,处理它们,然后使用2000个准备好的sql命令在一个事务中提交更改

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

已复制的更新表集=0

不可能:

这需要很多时间 由于MVCC,它复制了选项卡的大小 它在一个事务中完成:要么失败,要么通过。 在这种情况下,我实际上不需要事务特性:如果系统崩溃,它将只处理部分事务

其他几个问题: 做一个

update set replicated=0 where id >10000 and id<20000
速度也很慢,不过这应该是一件好事:按磁盘顺序遍历表

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

类似Mysql的更新限制不适用于PostgresQL

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

我说的行数是90000000行。数据库的大小可以是几兆字节

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


有什么想法吗?

添加一个新表来存储这个复制值,并添加一个主键来将每条记录链接到主表。然后,只需为每个已复制项添加一条记录,然后删除记录以删除已复制标志。或者反过来说,每个非复制记录都有一个记录,这取决于常见的情况


这也简化了当您想将它们全部设置回0时的情况,因为您只需截断表即可将磁盘上的表大小归零,如果您试图重置整个表,而不仅仅是几行,您甚至不必使用吸尘器来释放空间,在非常大的数据集上,它通常更快—而不是在常规表上,简单地创建表栏作为“选择所有内容”,但从foo复制0,然后交换表并删除旧表。显然,在执行此操作时,需要确保没有任何内容插入到原始表中。您还需要重新创建该索引

编辑:一个简单的改进,以避免在复制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;

在复制时让写入发生,并在事后插入。

这是伪代码。ints需要400MB,bigints临时文件需要800MB,如果有问题,可以使用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);

虽然你不可能解决空间使用的问题,但这只是暂时的,在出现真空之前,你可能真的可以在时钟时间方面加快这个过程。PostgreSQL使用MVCC这一事实意味着您应该能够做到这一点,而不会出现与新插入行相关的任何问题。CREATETABLE 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;

我认为最好把你的博士后改成8.X版。原因可能是Postgres的低版本。也可以尝试下面的查询。我希望这能有所帮助

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

我想你需要做的是 A.将2000条记录的PK值复制到具有相同标准限制的临时表中,等等。 B选择相同的2000条记录,并按原样在光标中执行必要的操作。 C如果成功,则对临时表中的记录运行单个更新查询。清除临时表并再次运行步骤a。 D如果不成功,请在不运行更新查询的情况下清除临时表。 简单、高效、可靠。 当做
KT

这是一个非常好的主意,尽管不幸的是它需要一个模式更改和一个长时间的更新过程。我真正喜欢这种方法的地方是,实际上,当前的部分索引在内部与这个想法非常相似!只有更加灵活和易于管理。
UPDATE table1 SET name = table2.value
FROM table2 
WHERE table1.id = table2.id;