14

PostgresQL に非常に大きなデータベース テーブルがあり、「コピー済み」のような列があります。すべての新しい行はコピーされずに開始され、後でバックグラウンド プログラムによって別のものに複製されます。そのテーブルには部分インデックス「btree(ID) WHERE replicated=0」があります。バックグラウンド プログラムは、最大 2000 エントリ (LIMIT 2000) の選択を行い、それらを処理してから、2000 の準備された SQL コマンドを使用して 1 つのトランザクションで変更をコミットします。

ここでの問題は、この複製された値をリセットするオプションをユーザーに提供し、すべてをゼロに戻すことです。

更新テーブル セット レプリケート = 0;

不可能です:

  • とても時間がかかります
  • MVCCのため、テーブルのサイズが重複しています
  • 1 つのトランザクションで実行されます。失敗するか、通過します。

この場合、実際にはトランザクション機能は必要ありません。システムがダウンした場合、システムはその一部のみを処理します。

その他のいくつかの問題:

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 順にテーブルを調べます...

(その場合、これをカバーする索引もあったことに注意してください)

(Mysql のような更新 LIMIT は PostgresQL では使用できません)

ところで: 本当の問題はもっと複雑で、既に展開されている組み込みシステムについて話しているので、リモートでスキーマを変更することは困難ですが、残念ながら PostgresQL 7.4 である可能性があります。

私が話している行の量は、たとえば 90000000 です。データベースのサイズは、数十ギガバイトになる場合があります。

データベース自体には 5 つのテーブルしか含まれておらず、そのうちの 1 つは非常に大きなものです。しかし、これは悪い設計ではありません。これらの組み込みボックスは、ERP システムなどではなく、1 種類のエンティティでのみ動作するからです。

何か案は?

4

6 に答える 6

9

このレプリケートされた値 (および各レコードをメイン テーブルにリンクする主キー) を格納する新しいテーブルを追加するのはどうでしょうか。次に、レプリケートされたアイテムごとにレコードを追加し、レコードを削除してレプリケート フラグを削除します。(または、その逆かもしれません - 一般的なケースに応じて、レプリケートされていないすべてのレコードのレコード)。

テーブルを切り捨てることができるので、それらをすべて0に戻したい場合も簡単になります(ディスク上のテーブルサイズをゼロにするため、スペースを解放するためにバキュームする必要さえありません)。

于 2008-09-21T21:44:55.123 に答える
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;

(コピー中に書き込みを行い、事後的に挿入します)。

于 2008-09-21T22:07:42.557 に答える
2

スペース使用量の問題を修正することはおそらく不可能ですが(一時的なものであり、真空になるまで)、おそらくクロック時間の観点からプロセスを実際にスピードアップすることができます。PostgreSQLがMVCCを使用しているという事実は、新しく挿入された行に関連する問題なしにこれを実行できるはずであることを意味します。selectとしてのcreatetableは、パフォーマンスの問題の一部を回避しますが、テーブルを継続して使用することはできず、同じくらいのスペースを必要とします。インデックスを捨てて再構築し、バキュームを実行するだけです。

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;
于 2008-09-22T18:46:14.543 に答える
1

これは疑似コードです。400MB (int の場合) または 800MB (bigint の場合) の一時ファイルが必要です (問題がある場合は、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);
于 2008-09-22T09:36:56.113 に答える
0

postgresをバージョン8.Xに変更する方が良いと思います。おそらく原因はPostgresの低バージョンです。以下のこのクエリも試してください。これがお役に立てば幸いです。

UPDATE table1 SET name = table2.value
FROM table2 
WHERE table1.id = table2.id;
于 2010-03-03T09:18:32.690 に答える
0

私はあなたがする必要があると思います. 2000 レコードの PK 値を、同じ標準制限などを持つ一時テーブルにコピーします。同じ2000件のレコードを選択し、そのままカーソルで必要な操作を行います。c. 成功した場合は、一時テーブルのレコードに対して単一の更新クエリを実行します。一時テーブルをクリアし、ステップ a を再度実行します。d. 失敗した場合は、更新クエリを実行せずに一時テーブルをクリアします。シンプルで効率的で信頼性があります。よろしく、 KT

于 2010-08-14T04:16:53.697 に答える