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のより高度な機能の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メーリングリストの中心的な問題を尋ねます。
より良いアイデアは完全に SQL Server FDW を使用することです。これは実際にMicrosoft SQL ServerをPostgreSQL形式にプルするロジックを持っています(たとえば、Bit
はBool
にマップされます)。この時点から
その後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)
を使用できます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 メールリストを参照してください。