web-dev-qa-db-ja.com

100k DDLステートメントのPostgres Transaction OOM

PostgreSQLでは、1回のトランザクションで約10万のDDLステートメントを実行します。実行中、それぞれのPostgres接続はメモリ使用量が徐々に増加し、それ以上メモリを取得できなくなると(3GB RAMで10MBから2.2GBに増加)、OOMキラーが9でヒットし、Postgresがリカバリモードになります。 。

BEGIN;

CREATE SCHEMA schema_1;
-- create table stmts - 714
-- alter table add pkey stmts - 714
-- alter table add constraint fkey stmts - 34
-- alter table add unique constraint stmts - 2
-- alter table alter column set default stmts - 9161
-- alter table alter column set not null stmts - 2405
-- alter table add check constraint stmts - 4
-- create unique index stmts - 224
-- create index stmts - 213

CREATE SCHEMA schema_2;
-- same ddl statements as schema_1 upto schema_7
-- ...
-- ...
-- ...
CREATE SCHEMA schema_7;

COMMIT

スキーマの作成ステートメントを含め、約94304のDDLステートメントが実行されることを想定していました。

のとおり、PostgreSQLのトランザクションDDL

いくつかの商用競合製品と同様に、PostgreSQLのより高度な機能の1つは、先行書き込みログ設計を介してトランザクションDDLを実行する機能です。この設計は、テーブルの作成など、DDLへの大きな変更でさえバックアウトをサポートします。データベースまたはテーブルスペースの追加/削除から回復することはできませんが、他のすべてのカタログ操作は元に戻すことができます。

約35GBのデータを問題なく1つのトランザクションでPostgreSQLにインポートしましたが、何千ものDDLステートメントを1つのトランザクションで実行するときにPostgres接続に巨大なメモリが必要なのはなぜですか?

RAMを増やすかスワップを割り当てることで一時的に解決できますが、1つのトランザクションでのスキーマ作成の数は最大50〜60(約1M DDLステートメント)増える可能性があり、100を必要とするでしょう。 +ギグRAMまたは現在実行できないスワップ。

PostgreSQLバージョン:9.6.10

dmlステートメントでは必要ないのに、多くのDDLステートメントを実行するにはより多くのメモリが必要になる理由はありますか?どちらも、基になるWALに書き込むことでトランザクションを処理しませんか?では、なぜDLLの場合は異なるのですか?

単一取引の理由

お客様のデータベース全体をCustomer Premise(SQL Server)からクラウド(PostgreSQL)に同期します。すべてのお客様が異なるデータベースを持っています。プロセスは、データ全体がSQL ServerからCSVとして生成され、一時テーブル、COPYおよびON CONFLICT DO UPDATEを使用してPostgreSQLにインポートされます。このプロセスでは、各顧客をPG内の単一のデータベースとして扱い、顧客のSQL Server内の個々のDBを顧客のPG DB内のスキーマとして扱います。

したがって、CSVデータに基づいて、スキーマを動的に作成し、そこにデータをインポートします。アプリケーションの設計に従って、PG内のデータは常に厳密に一貫している必要があり、部分的なスキーマ/テーブル/データがあってはなりません。したがって、これを単一のトランザクションで達成する必要がありました。また、3分ごとに顧客からクラウドDBに段階的に同期します。したがって、スキーマの作成は、最初の同期または増分同期のいずれかで行うことができます。しかし、最初の同期自体で非常に多くのスキーマを作成する可能性は非常に高いです。

更新1

ALTER TABLE ALTER COLUMNステートメントにコメントを付けると、メモリの使用量が大幅に削減されました。それらをCREATE TABLEステートメント自体にマージする必要があります。

PG Hackersメーリングリストの中心的な問題を尋ねます。

7
The Coder

より良いアイデアは完全に SQL Server FDW を使用することです。これは実際にMicrosoft SQL ServerをPostgreSQL形式にプルするロジックを持っています(たとえば、BitBoolにマップされます)。この時点から

その後3分ごとに

  • 外部スキーマを_last_fetch_schema_にインポートします
  • _last_fetch_schema_が_local_schema_ と異なる場合
    • スキーマを再同期する
  • _INSERT INTO ... SELECT ON CONFLICT DO UPDATE_を使用してすべてのデータをコピーすると、最新のデータのみを選択できます。
  • 外部スキーマを削除する_last_fetch_schema_

あなたは何を得るのですか?

  • 最初のロードでは、単純にCREATE TABLE local.foo ( LIKE foreign.foo)を使用できます
  • メタデータの違いを簡単に比較できます
  • CSVは型を失い、推論する必要があります。FDWはメタデータカタログを読み取ることができます。
  • 行がバージョン管理されている場合、最新のものだけを取得するのは非常に簡単です。データベース全体を送信する必要がなくなります。
4
Evan Carroll

Src/backend/utils/cache/relcache.cのこのコメントのコメントは関連があるようです:

    * If we Rebuilt a relcache entry during a transaction then its
    * possible we did that because the TupDesc changed as the result
    * of an ALTER TABLE that ran at less than AccessExclusiveLock.
    * It's possible someone copied that TupDesc, in which case the
    * copy would point to free'd memory. So if we rebuild an entry
    * we keep the TupDesc around until end of transaction, to be safe.
    */
    if (remember_tupdesc)
        RememberToFreeTupleDescAtEOX(relation->rd_att);

ポインタがあるかもしれないこの「誰か」は誰なのか、私はそれを本当に理解していませんか?これは共有メモリではなくプライベートメモリです。とにかく、同じトランザクションのすべての「alter table」ステートメントがそのテーブルのTupDescの別のコピーを残すので、それは膨張を説明しているようです。そして、明らかに、1つのalter tableで複数のアクションを使用する場合でも、個別のアクションごとにコピーが残ります。しかし、メリットが何であれ、これはメモリ使用量の大部分を説明しています。

詳細については pg hackers メールリストを参照してください。

3
jjanes