2008-09-21 60 views
13

我在PostgresQL中有一个非常大的数据库表和一个像“复制”的列。每一个新的行开始uncopied,并将在后来通过后台程序复制到另一个东西。在该表“btree(ID)WHERE replicated = 0”上有部分索引。后台程序为最多2000个条目(LIMIT 2000)进行选择,对它们进行处理,然后使用2000个准备好的sql-commands在一个事务中提交更改。更新非常大的PostgreSQL数据库表有效

现在的问题是,我想给用户一个选项来重置此复制值,使其全部为零。

更新表set replicated = 0;

是不可能的:

  • 这需要非常多的时间
  • 它只是重复,因为MVCC
  • 它是在一个事务中所做的TABEL的大小:它要么失败或经历。

我实际上并不需要这种情况下的事务功能:如果系统关闭,它只会处理它的一部分。

其他几个问题: 做一个

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) 

也慢,但它应该是一件好事:通过磁盘顺序表去...

(注意,在这种情况下,也有涵盖这一变化的指数)

(如MySQL的更新限制是对PostgreSQL不可用)

BTW:真正的问题是比较复杂的,我们正在谈论的是这里已经部署了嵌入式系统,所以远程模式更改很困难,但是可能的不幸的是,它可能是PostgresQL 7.4。

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

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

任何想法?

回答

8

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

如果您想将它们全部设置为0,那么也可以简化这种情况,因为您可以截断表格(将磁盘上的表格大小归零,您甚至不必抽空以释放空间)

+0

这是一个非常好的主意,虽然它不幸需要架构更改(长期更新过程)。 我真的很喜欢这种方法,实际上,当前的部分索引在内部非常类似于这个想法!只有更灵活和可管理。 – Christian 2008-09-21 21:52:42

3

如果您试图重置整个表格,而不是几行,则通常会更快(对于非常大的数据集 - 而不是常规表格),只需要CREATE TABLE bar AS SELECT everything, but, copied, 0 FROM foo,然后交换表格并放弃旧的一。很明显,您需要确保在执行此操作时没有任何内容插入到原始表格中。您还需要重新创建该索引。

编辑:为了避免锁定表的简单改进,而你复制14千兆字节:

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; 

(让,而你正在做的副本写入发生,并插入-呈文后)。

1

这是伪代码。您需要400MB(用于整数)或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); 
2

虽然你不能修复可能空间使用率的问题(这是暂时的,只是直到真空),你可能真正的时钟时间加快这一进程。 PostgreSQL使用MVCC这一事实意味着您应该能够在没有任何与新插入的行相关的问题的情况下执行此操作。 create table作为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; 
1

我认为最好将postgres改为8.X.可能原因是Postgres的低版本。也请尝试下面的查询。我希望这可以帮助。

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

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