0Pricing
PostgreSQL Performance & Query Optimization · Lektion

WAL-Erzeugung und Schreibverstärkung

Messen und reduzieren Sie das WAL-Volumen pro Transaktion, um I/O- und Replikationskosten zu senken.

WAL-Erzeugung und Schreibverstärkung ist eine kostenlose PostgreSQL Performance & Query Optimization-Lektion auf CoddyKit. Dies ist Lektion 4 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des PostgreSQL Performance & Query Optimization-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der PostgreSQL Performance & Query Optimization-Kurs umfasst insgesamt 4 Lektionen.

Teile dieser Lektion wurden noch nicht übersetzt und werden auf Englisch angezeigt.

Why WAL Volume Matters

Every change in PostgreSQL is first written to the Write-Ahead Log (WAL) before it touches the data files. WAL guarantees durability and crash recovery, but the bytes it produces are not free.

  • Disk I/O: WAL is fsynced on commit, so high WAL volume means more write throughput pressure.
  • Replication: every WAL byte must be shipped to replicas and read by logical decoders.
  • Backups: archived WAL (via archive_command or pg_receivewal) grows storage and restore time.

This lesson is about measuring the WAL each transaction generates and reducing it without sacrificing durability.

What Actually Generates WAL

WAL records are emitted for far more than just your row changes. Knowing the sources is the first step to cutting volume.

  • Heap/index changes: inserts, updates, deletes, and the index entries they touch.
  • Full Page Images (FPIs): the first write to a page after a checkpoint logs the entire 8 KB page, not just the change.
  • HOT pruning and visibility map updates.
  • Hint bit / freeze writes during VACUUM.

FPIs are usually the single biggest and most surprising contributor to write amplification.

Measuring WAL Per Statement with EXPLAIN

Since PostgreSQL 13, EXPLAIN (ANALYZE, WAL) reports exactly how much WAL a statement produced: the number of records, the number of full page images, and the total bytes.

This is the most precise tool for attributing WAL to a specific query. Watch the fpi count closely — a high FPI count signals checkpoint-driven amplification.

EXPLAIN (ANALYZE, BUFFERS, WAL)
UPDATE orders
SET status = 'shipped'
WHERE shipped_at IS NULL
  AND created_at < now() - interval '1 day';

Reading the WAL Line

The WAL line in the plan looks like this:

  • WAL: records=12043 fpi=512 bytes=4823104

Interpretation:

  • records: total WAL records emitted (one or more per tuple touched).
  • fpi: full page images — each is roughly 8 KB, so 512 FPIs ≈ 4 MB of the total alone.
  • bytes: the grand total written to WAL.

If fpi × 8192 is a large fraction of bytes, your amplification is dominated by full page images, not by the logical change itself.

Cluster-Wide WAL with pg_stat_wal

For a cluster-level view, pg_stat_wal (PostgreSQL 14+) aggregates WAL generation. Sample it, run a workload, sample again, and diff.

Key columns: wal_records, wal_fpi, and wal_bytes. A rising wal_fpi rate between checkpoints confirms FPI-driven amplification at the system level.

SELECT wal_records, wal_fpi, pg_size_pretty(wal_bytes) AS wal_size
FROM pg_stat_wal;

Measuring Raw Volume with WAL LSNs

You can also measure WAL produced over any interval by differencing the current Log Sequence Number (LSN). The LSN is a monotonic byte offset into the WAL stream.

Capture pg_current_wal_lsn() before and after a workload, then subtract. This counts ALL WAL, including background work like autovacuum.

SELECT pg_size_pretty(
  pg_wal_lsn_diff('0/9A3B1200', '0/95C40000')
) AS wal_generated;

Full Page Images and Checkpoint Timing

An FPI is written on the first modification of a page after a checkpoint. So checkpoints that fire too often force the same hot pages to be re-imaged repeatedly.

The fix is to spread checkpoints out:

  • Raise max_wal_size so checkpoints are triggered by volume less aggressively.
  • Raise checkpoint_timeout (e.g. 15min) so fewer time-based checkpoints occur.
  • Keep checkpoint_completion_target near 0.9 to smooth the flush, not to reduce FPIs.

Fewer checkpoints means each hot page is imaged once across a longer window — a direct cut in WAL bytes.

ALTER SYSTEM SET max_wal_size = '8GB';
ALTER SYSTEM SET checkpoint_timeout = '15min';
ALTER SYSTEM SET checkpoint_completion_target = 0.9;
SELECT pg_reload_conf();

wal_compression: Shrinking FPIs

When FPIs are unavoidable, you can compress them. wal_compression compresses full page images before they are written to WAL.

  • off: no compression (default on older versions).
  • pglz: cheap, modest ratio.
  • lz4 / zstd (PostgreSQL 15+): better ratios, zstd for maximum reduction.

This trades a little CPU for potentially large WAL savings on FPI-heavy workloads. It does NOT compress regular WAL records, only the page images.

ALTER SYSTEM SET wal_compression = 'zstd';
SELECT pg_reload_conf();

-- Verify the change took effect
SHOW wal_compression;

HOT Updates Cut Index WAL

A Heap-Only Tuple (HOT) update avoids writing new index entries when no indexed column changes and the new tuple fits on the same page. Fewer index writes means less WAL.

Two levers maximize HOT:

  • Don't update indexed columns when you don't have to — narrow your SET list.
  • Leave free space on pages with a lower fillfactor so the new tuple version stays on the same page.

Check the n_tup_hot_upd ratio to confirm HOT is actually firing.

SELECT relname,
       n_tup_upd,
       n_tup_hot_upd,
       round(100.0 * n_tup_hot_upd / NULLIF(n_tup_upd, 0), 1) AS hot_pct
FROM pg_stat_user_tables
ORDER BY n_tup_upd DESC
LIMIT 10;

Batching and Unlogged Tables

Two more structural reductions:

  • Batch writes: one large INSERT ... SELECT or COPY produces far less WAL overhead than thousands of single-row transactions, because per-record and per-commit overhead is amortized.
  • UNLOGGED tables: skip WAL entirely for scratch, staging, or derived data. The tradeoff is that the table is truncated after a crash and is NOT replicated.

Use unlogged tables only where losing the data on crash is acceptable — ETL staging, materialized scratch, session caches.

CREATE UNLOGGED TABLE staging_events (
    id        bigint,
    payload   jsonb,
    loaded_at timestamptz DEFAULT now()
);

Reducing Write Amplification by Design

Beyond knobs, schema and access patterns drive WAL volume:

  • Avoid wide updates: updating a row rewrites the whole tuple plus FPIs for its page. Split rarely-updated wide columns into a side table.
  • Lower fillfactor on hot tables (e.g. 80) to keep HOT updates alive.
  • Prune redundant indexes: every index multiplies write WAL.
  • Use COPY for bulk loads and consider the COPY ... FREEZE path for fresh tables.

Each design choice compounds: fewer FPIs, fewer index entries, fewer commits.

ALTER TABLE orders SET (fillfactor = 80);
-- Existing rows take effect after a rewrite
VACUUM FULL orders;

Quick Check: Diagnosing High WAL

You run EXPLAIN (ANALYZE, WAL) on a batch UPDATE and see WAL: records=20000 fpi=9800 bytes=82000000. The FPIs account for roughly 80 MB of the 82 MB total. Which single change most directly reduces this WAL volume?

Recap: Measure Then Reduce

You now have a full WAL-reduction toolkit:

  • Measure: EXPLAIN (ANALYZE, WAL) per statement, pg_stat_wal cluster-wide, and pg_wal_lsn_diff() for raw volume over an interval.
  • Read the signal: high fpi means checkpoint-driven amplification; high records with low fpi means logical change volume.
  • Reduce FPIs: raise max_wal_size and checkpoint_timeout; enable wal_compression (zstd/lz4).
  • Reduce records: favor HOT updates (narrow SET lists, lower fillfactor), batch writes, drop redundant indexes.
  • Skip WAL: UNLOGGED tables for disposable data.

Always measure first — attribute the bytes before you turn any knob.

Häufig gestellte Fragen

Ist die Lektion „WAL-Erzeugung und Schreibverstärkung“ kostenlos?

Ja — der vollständige Text von „WAL-Erzeugung und Schreibverstärkung“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des PostgreSQL Performance & Query Optimization-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der PostgreSQL Performance & Query Optimization-Kurs umfasst insgesamt 4 Lektionen.

Was lerne ich in „WAL-Erzeugung und Schreibverstärkung“?

Messen und reduzieren Sie das WAL-Volumen pro Transaktion, um I/O- und Replikationskosten zu senken. Du übst PostgreSQL Performance & Query Optimization mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.

Brauche ich Erfahrung, um PostgreSQL Performance & Query Optimization zu starten?

Keine Vorkenntnisse erforderlich. PostgreSQL Performance & Query Optimization auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 4 von 4.

Wie lange dauert die Lektion „WAL-Erzeugung und Schreibverstärkung“?

Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.

Kann ich in dieser PostgreSQL Performance & Query Optimization-Lektion Code schreiben und ausführen?

Ja. Jede PostgreSQL Performance & Query Optimization-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.

Alle Lektionen in diesem Kurs

  1. Tupel-Sichtbarkeit, xmin und xmax
  2. HOT-Updates und Heap-Only-Tupelketten
  3. Die Visibility Map und Index-Only-Scans
  4. WAL-Erzeugung und Schreibverstärkung
← Zurück zu PostgreSQL Performance & Query Optimization