特定の操作がどのようにしてhuge量のWALファイルを生成するかを常に驚いています。
私はこれらのWALファイルをポイントインタイムリカバリにしたいので(さらに、毎晩フルダンプを実行します)、提供されている基本的な機能はwantedであり、変更したくありません(つまり、WALアーカイブをオフにする方法などは探していません。
これらの設定でPostgres 9.5を使用する:
wal_level = archive
checkpoint_timeout = 20min
max_wal_size = 1GB
min_wal_size = 80MB
archive_command = 'test ! -f /backup/wal/%f && cp %p /backup/wal/%f'
私は今日このステートメントを実行しました:
WITH table2_only_names AS (
SELECT id , name FROM table2
)
UPDATE table1
SET table2_name = table2_only_names.name
FROM table2_only_names
WHERE table1.table2_id = table2_only_names.id;
CREATE TABLE public.table1 (
id BIGINT PRIMARY KEY NOT NULL DEFAULT nextval('table1_id_seq'::regclass),
table2_id BIGINT,
table3_id BIGINT,
positive_count INTEGER NOT NULL DEFAULT 0,
neutral_count INTEGER NOT NULL DEFAULT 0,
negative_count INTEGER NOT NULL DEFAULT 0,
is_blocked BOOLEAN NOT NULL DEFAULT false,
blocker_id BIGINT,
group BIGINT,
created TIMESTAMP WITH TIME ZONE,
modified TIMESTAMP WITH TIME ZONE,
table4_id BIGINT,
name CHARACTER VARYING(255)
);
CREATE UNIQUE INDEX idx1 ON table1 USING BTREE (table3_id, table2_id);
CREATE INDEX idx2 ON table1 USING BTREE (table3_id);
CREATE INDEX idx3 ON table1 USING BTREE (table2_id);
CREATE INDEX idx4 ON table1 USING BTREE (group);
CREATE INDEX idx5 ON table1 USING BTREE (blocker_id);
CREATE INDEX idx6 ON table1 USING BTREE (table4_id);
15 Mio行、テーブルサイズ〜3,4GB、インデックスサイズ7GB
CREATE TABLE public.table2 (
id BIGINT PRIMARY KEY NOT NULL DEFAULT nextval('table2_id_seq'::regclass),
name CHARACTER VARYING(255),
);
10 Mio行、テーブルサイズ〜2GB、インデックスサイズ3,4GB
実行時間は約55分でした(ここでは不満はありません)。
生成されたWALファイルの量は予想外に膨大でした。前述のWALアーカイブは100 GBの専用パーティション上にあり、クエリが開始した時点では、約30 GBは無料でした。
15〜20分後にディスク領域が<10GBになり、「古い」WALアーカイブを削除し始めたので、十分ではありませんでした。このステートメントによって生成されたWALアーカイブファイルをすでに削除する必要があると確信していた場合、私はこれまで絶えずこれを行わなければなりませんでした。
問題のテーブルは、その間、他のプロセスでは使用されませんでしたが、他のテーブルの「通常の」操作は続行されました。
クエリを開始した時刻と終了した時刻をマークしました。 WALアーカイブファイルを削除した場所を明確に確認できます。
私には、なぜこれほど多くのものが生成されるのかは謎であり、どれほどの量が必要で、いつ操作がストライクするかが予測できないため、現在は問題となっています。
必要なスペースの量をよりよく理解するために何が欠けていますか?これらは回避できますか?私は何か間違ったことをしていますか?
PostgreSQLで行を更新すると、通常、更新された列だけでなく行全体のコピーが作成され、古い行が削除済みとしてマークされます。新しいコピーでは、WAL全体をログに記録する必要があります。 full_page_writesがオンになっていて、チェックポイントの間隔が狭すぎる場合は、平均して古い行もWALでログに記録されます。
更新された行のほとんどすべてが、その行のすべてのインデックスも更新する必要があるでしょう。これは、行の新しいバージョンが古いバージョンと同じページに収まらないため、インデックスは新しいバージョンを見つける場所を知っている必要があるためです。
したがって、テーブル全体を2回(古い行で1回、新しい行で1回)、すべての場合はインデックスもログに記録します。また、WALレコードにはかなりのオーバーヘッドがあります。また、full_page_writesをオンにしてチェックポイントを頻繁に行うと、事態はさらに悪化します。
1)更新の多くが縮退している(すでに更新されている値に更新されている)場合は、追加のwhere句を使用してそれらの更新を抑制できます。
WITH table2_only_names AS (
SELECT id , name FROM table2
)
UPDATE table1
SET table2_name = table2_only_names.name
FROM table2_only_names
WHERE table1.table2_id = table2_only_names.id
AND table2_name is distinct from table2_only_names.name;
2)ほとんどのWALファイルは非常に圧縮可能です。次のような圧縮コマンドをarchive_commandに含めることができます
archive_command = 'set -C -o pipefail; xz -2 -c %p > /backup/wal/%f.xz'
もちろん、recovery_commandを逆に実行する必要があります。
3)9.5を使用しているため、wal_compressionをオンにしてみてください。
4)full_page_writesをオフにしてみることもできますが、ほとんどのストレージハードウェアでは、クラッシュした場合にデータが破損するリスクがあります。または、この操作中にチェックポイントが頻繁に発生する場合は、チェックポイントが頻繁に発生しないようにして、full_page_writesをオンにした場合の影響を少なくすることができます。