Deep EVM #28: Hochdurchsatz-Datenpipeline — Batch-Inserts, COPY und Konfliktloesung
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.