본문으로 건너뛰기
DevOpsMar 28, 2026

Deep EVM #28: Hochdurchsatz-Datenpipeline — Batch-Inserts, COPY und Konfliktloesung

OS
Open Soft Team

Engineering Team

Das Problem: Milliarden von Zeilen schnell einfuegen

Ein Blockchain-Indexer, der Millionen von Bloecken verarbeitet, muss Milliarden von Zeilen so schnell wie moeglich einfuegen. Einzelne INSERT-Anweisungen erreichen maximal 5.000 Zeilen pro Sekunde; das COPY-Protokoll schafft 250.000+ Zeilen pro Sekunde.

COPY-Protokoll

COPY ist PostgreSQLs schnellster Weg, Daten zu laden:

use tokio_postgres::CopyInSink;
use bytes::BytesMut;

async fn bulk_insert(
    client: &Client,
    transactions: &[Transaction],
) -> Result<u64, Error> {
    let sink = client.copy_in(
        "COPY transactions (block_number, tx_hash, from_addr, value_wei) \
         FROM STDIN BINARY"
    ).await?;
    
    let writer = BinaryCopyInWriter::new(sink, &[
        Type::INT8,   // block_number
        Type::BYTEA,  // tx_hash
        Type::BYTEA,  // from_addr
        Type::NUMERIC, // value_wei
    ]);
    
    pin_mut!(writer);
    for tx in transactions {
        writer.as_mut().write(&[
            &tx.block_number,
            &tx.tx_hash.as_ref(),
            &tx.from_addr.as_ref(),
            &tx.value_wei,
        ]).await?;
    }
    
    writer.finish().await
}

Bulk-Upsert-Muster

Fuer Daten mit moeglichen Duplikaten:

-- Temporaere Staging-Tabelle (ungeloggt = schneller)
CREATE UNLOGGED TABLE staging_transactions (LIKE transactions INCLUDING ALL);

-- Daten per COPY in Staging laden
COPY staging_transactions FROM STDIN BINARY;

-- Upsert von Staging in Zieltabelle
INSERT INTO transactions
SELECT * FROM staging_transactions
ON CONFLICT (tx_hash) DO UPDATE SET
    value_wei = EXCLUDED.value_wei;

-- Staging bereinigen
TRUNCATE staging_transactions;

WAL-Tuning

Write-Ahead-Log-Konfiguration fuer Hochdurchsatz:

# postgresql.conf
wal_level = minimal           # Wenn keine Replikation noetig
max_wal_size = 8GB            # Groessere WAL-Segmente
checkpoint_completion_target = 0.9
commit_delay = 100            # Mikrosekunden Verzoegerung fuer Group Commit

Connection Pooling mit PgBouncer

# pgbouncer.ini
[databases]
mydb = host=localhost dbname=mydb

[pgbouncer]
pool_mode = transaction
max_client_conn = 1000
default_pool_size = 50

Transaction-Pooling-Modus: Verbindungen werden nach jeder Transaktion freigegeben, was 1.000 Clients mit 50 DB-Verbindungen bedient.

Monitoring mit pg_stat_statements

-- Langsamste Abfragen finden
SELECT
    query,
    calls,
    mean_exec_time,
    total_exec_time
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 20;

Fazit

Hochdurchsatz-Datenpipelines mit PostgreSQL erfordern das COPY-Protokoll fuer maximale Einfuegegeschwindigkeit, Bulk-Upsert-Muster fuer Duplikatbehandlung, WAL-Tuning fuer Schreibdurchsatz, PgBouncer fuer effizientes Connection Pooling und pg_stat_statements fuer Monitoring. Die Kombination dieser Techniken verwandelt PostgreSQL von einer Standard-Datenbank in eine Hochleistungs-Datenpipeline.