At 1M+ change events per second, the bottlenecks shift from configuration to architecture. The database log becomes a firehose, Kafka partitioning strategy determines whether you can parallelize, and your downstream materialization system must keep up without unbounded memory growth. This guide covers what actually limits throughput at scale — and how to architect both Debezium and RisingWave for peak performance.
What "1M Events/Second" Means in Practice
Before optimizing, understand what generates this volume. A system producing 1M change events/second is experiencing:
- ~86 billion events per day
- Roughly 500 MB/sec to 2 GB/sec of WAL, depending on row size
- Significant CPU pressure on the source database from WAL decoding
Most production systems with this load are financial trading platforms, high-frequency e-commerce systems, or telemetry ingestion pipelines. Standard operational databases rarely hit this rate; more commonly, teams hit 50K-200K events/second and want to plan for 10x growth.
Debezium Scaling Architecture
A single Debezium connector is single-threaded per table partition. This is the fundamental constraint.
Parallelism via Multiple Connectors
The primary way to scale Debezium horizontally is to run multiple connectors, each covering a subset of tables. Kafka Connect distributes connector tasks across worker nodes.
// Connector 1: high-volume transactional tables
{
"name": "orders-connector",
"config": {
"connector.class": "io.debezium.connector.postgresql.PostgresConnector",
"table.include.list": "public.orders,public.order_items",
"tasks.max": "1"
}
}
// Connector 2: customer and product tables
{
"name": "catalog-connector",
"config": {
"connector.class": "io.debezium.connector.postgresql.PostgresConnector",
"table.include.list": "public.customers,public.products",
"tasks.max": "1"
}
}
Note: tasks.max for a Debezium PostgreSQL connector is always 1 per connector instance. PostgreSQL logical replication uses a single stream per slot. You cannot parallelize within a single connector for a single database.
For PostgreSQL specifically, the only way to get more than one parallel stream is to have multiple databases (or use Citus/partitioned databases where each shard is its own logical database).
Kafka Partition Strategy
Debezium writes to Kafka. Kafka partitions determine downstream parallelism. By default, Debezium partitions by primary key hash, which means all events for a given row go to the same partition — preserving per-row ordering.
At high throughput, you may want more partitions:
{
"key.converter": "org.apache.kafka.connect.json.JsonConverter",
"producer.override.batch.size": "524288",
"producer.override.linger.ms": "50",
"producer.override.compression.type": "lz4",
"producer.override.acks": "1"
}
These producer settings trade some durability for throughput:
batch.size=524288: 512KB batches (default is 16KB)linger.ms=50: wait 50ms to fill batches before sendingcompression.type=lz4: fast compression; typically 3-5x compression ratio on JSON CDC eventsacks=1: acknowledge from leader only (not all replicas)
At 1M events/sec, the difference between acks=all and acks=1 can be 2-3x throughput.
Kafka Connect Worker Scaling
Kafka Connect's distributed mode runs connectors across a cluster of workers. More workers = more tasks can run in parallel. But Debezium connectors are inherently single-task, so adding workers only helps if you have more connectors to distribute.
Benchmark results from community reports (Debezium 2.x + Kafka 3.x, 3-node Kafka Connect cluster):
| Setup | Sustained Throughput |
| Single connector, small rows (200 bytes) | 80-120K events/sec |
| Single connector, medium rows (1KB) | 40-60K events/sec |
| 10 connectors, dedicated workers | 400-600K events/sec |
| 20 connectors, optimized batching | 800K-1.2M events/sec |
These figures are from community benchmarks and production reports on the Debezium mailing list and Confluent blogs — not official Debezium benchmarks.
Where Debezium Struggles at Scale
WAL decoding CPU: At very high event rates, PostgreSQL spends significant CPU decoding WAL for the logical replication stream. This is unavoidable — logical decoding runs on the primary. At 500K+ events/sec, WAL decoding can consume 1-2 CPU cores on the source database.
Offset commit frequency: Kafka Connect commits offsets periodically. At high throughput, infrequent offset commits mean large re-processing windows on restart. Tune carefully:
offset.flush.interval.ms=5000
offset.flush.timeout.ms=10000
Back-pressure: If downstream consumers (Kafka Streams, Flink, RisingWave) can't keep up, Kafka topic lag grows. Debezium itself doesn't slow down — it keeps writing to Kafka. The WAL slot advances (good: no disk accumulation), but topic lag grows until consumers catch up.
RisingWave Scaling Architecture
RisingWave is a distributed system. It scales horizontally by adding compute nodes. The CDC ingestion layer and the stream processing layer scale independently.
Parallelism Configuration
When creating a CDC source or materialized view in RisingWave, the parallelism parameter controls how many parallel workers process the stream:
-- Create source with parallelism hint
CREATE SOURCE orders_source WITH (
connector = 'postgres-cdc',
hostname = 'db',
port = '5432',
username = 'rwuser',
password = 'secret',
database.name = 'prod',
schema.name = 'public',
table.name = 'orders'
);
-- Materialized view with explicit parallelism
CREATE MATERIALIZED VIEW orders_summary
WITH (parallelism = 8)
AS
SELECT
status,
count(*) AS cnt,
sum(total_amount) AS total
FROM orders_source
GROUP BY status;
More parallelism workers distribute the computation across available CPU cores and nodes. Adding RisingWave compute nodes automatically increases available parallelism capacity.
Checkpoint Frequency and Throughput Tradeoff
RisingWave uses checkpoint-based exactly-once semantics. Checkpoints are snapshots of operator state persisted to S3 (or MinIO for self-hosted).
At high throughput, checkpoint frequency becomes a tuning lever:
-- Check current checkpoint interval
SHOW streaming_parallelism;
-- Set checkpoint frequency (via system parameter)
ALTER SYSTEM SET checkpoint_frequency = 1;
Higher checkpoint frequency (more frequent checkpoints) means:
- Lower recovery lag if a failure occurs
- Higher S3 write throughput
- Slightly more overhead per checkpoint
Lower checkpoint frequency means:
- Less S3 I/O overhead
- Longer recovery time if a failure occurs
- Higher sustained throughput during normal operation
At 1M events/sec, a checkpoint every 10 seconds means each checkpoint captures 10M events worth of state delta. With a typical 1KB state per key and 100K unique keys, checkpoint size is manageable (~100MB). For larger state (more keys, wider rows), increasing checkpoint interval to 30-60 seconds is common.
Memory Management
RisingWave stores in-flight stream state in memory, spilling to S3 when necessary. The key parameter is the memory limit per executor:
# risingwave configuration
RW_STREAMING_MEMORY_LIMIT=8GiB
At 1M events/sec with aggregation operators, RisingWave automatically manages memory by spilling operator state to S3 when needed — ensuring the system remains stable under load without manual intervention. A windowed aggregation over a 1-minute window with 100K unique keys at 1KB state per key requires approximately 100MB of working memory. Scaling to 1M unique keys requires 1GB; simply add a compute node and RisingWave redistributes the workload automatically.
How RisingWave Handles High-Throughput CDC
RisingWave's distributed architecture is built for scale. Each compute node independently processes its assigned stream partitions — adding nodes linearly increases processing capacity without any reconfiguration.
Elastic horizontal scaling: Set parallelism on materialized views to distribute computation across all available nodes. Adding nodes automatically expands parallelism capacity. There is no manual rebalancing.
Disaggregated storage: RisingWave's checkpoint state is stored in S3 (or MinIO), completely separate from compute. This means scaling compute up or down requires no data migration. State is always safe in object storage.
SQL-native optimization: Cascaded materialized views let you pre-aggregate at each processing stage using standard SQL — no custom code, no framework-specific configuration. The same SQL patterns that work at 100K events/sec work at 1M events/sec.
What "Throughput" Means in Each System
Raw ingestion throughput comparisons between Debezium+Kafka and RisingWave measure fundamentally different things. Debezium's throughput is measured at the Kafka producer — the rate at which raw change events land in Kafka topics with no transformation. RisingWave's throughput is measured end-to-end: from WAL ingestion through stateful joins and aggregations to queryable materialized view results.
Debezium + Kafka at high throughput delivers raw events. A separate consumer (Flink, Spark, custom application) still needs to process those events. RisingWave at the same event rate delivers fully processed, query-ready results — eliminating the consumer layer entirely.
For workloads that require real-time aggregations, joins, or derived views, RisingWave's end-to-end architecture is the more meaningful comparison point. Always benchmark against your specific workload and query patterns.
Architecture Recommendation at Scale
For systems genuinely at 1M+ events/second:
Shard the source database: A single PostgreSQL instance with 1M writes/sec is unusual. More likely, you have a sharded or partitioned setup. Each shard gets its own Debezium connector or RisingWave CDC source.
Separate ingestion from processing: Use Debezium to write to Kafka for durable buffering, then feed RisingWave from Kafka. This decouples ingestion rate from processing rate and allows independent scaling.
-- RisingWave consuming from Kafka (not directly from PostgreSQL)
CREATE SOURCE orders_kafka_source WITH (
connector = 'kafka',
topic = 'cdc.orders',
properties.bootstrap.server = 'kafka:9092',
scan.startup.mode = 'earliest'
) FORMAT DEBEZIUM ENCODE JSON;
- Use RisingWave for stateful processing: Kafka handles durable buffering. RisingWave handles stateful joins, aggregations, and materialized views. Each system does what it does best.
FAQ
Is 1M CDC events/second achievable with a single PostgreSQL instance? It requires a very high-spec server. PostgreSQL WAL decoding for logical replication at this rate typically requires 4-8 CPU cores dedicated to WAL sender processes, fast NVMe storage, and 10GbE networking. Most production PostgreSQL instances that see this kind of write load are write-optimized (AWS Aurora, Citus clusters, or TimescaleDB hypertables) rather than single-instance setups.
Does Debezium have flow control / back-pressure? Debezium itself doesn't throttle based on downstream Kafka consumer lag. It reads WAL and writes to Kafka at the rate WAL is generated (or faster, during catch-up). If Kafka is full or slow, Kafka producer back-pressure propagates to the Debezium connector thread, which then slows WAL consumption. This is the natural flow control mechanism.
How does RisingWave handle a CDC source that temporarily falls behind?
RisingWave buffers unprocessed events in memory and on the WAL reader side. When a checkpoint completes, the replication slot advances. If RisingWave falls very far behind, the PostgreSQL replication slot retains more WAL. The same operational risk applies as with Debezium: monitor wal_retained in pg_replication_slots.
What's the per-event overhead in RisingWave vs Debezium+Kafka? Debezium's per-event overhead is primarily serialization (JSON or Avro) and network round-trip to Kafka. RisingWave's per-event overhead includes WAL decoding, deserialization, operator evaluation, and periodic state serialization for checkpointing. For raw ingestion throughput, Debezium+Kafka is typically faster. For end-to-end latency from DB change to queryable result, RisingWave is typically lower because it eliminates the consumer-side polling step.
Should I use RisingWave or Flink for high-throughput CDC processing? For SQL-centric workloads with complex aggregations and joins, both are viable. RisingWave's PostgreSQL compatibility means existing SQL skills transfer directly. Flink has a larger ecosystem for custom operators and multi-source joins. For teams already fluent in PostgreSQL SQL, RisingWave typically requires less operational expertise to scale.

