高效更新 VERY LARGE PostgreSQL 数据库表
Posted
技术标签:
【中文标题】高效更新 VERY LARGE PostgreSQL 数据库表【英文标题】:Update VERY LARGE PostgreSQL database table efficiently 【发布时间】: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 系统或类似的东西!
有什么想法吗?
【问题讨论】:
【参考方案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;
【讨论】:
以上是关于高效更新 VERY LARGE PostgreSQL 数据库表的主要内容,如果未能解决你的问题,请参考以下文章
:Introduction to Very Large Databases 读书笔记
:Introduction to Very Large Databases 读书笔记
从 url 列表中下载 <very large> 页面的最佳方法是啥?
VGGnet论文总结(VERY DEEP CONVOLUTIONAL NETWORKS FOR LARGE-SCALE IMAGE RECOGNITION)
VERY DEEP CONVOLUTIONAL NETWORKS FOR LARGE-SCALE IMAGE RECOGNITION(VGG网络)-论文阅读笔记