The OLAP / OLTP Split Is Back
Around 2018, the database industry went through a phase of HTAP optimism. Hybrid Transactional/Analytical Processing — the idea that one database could handle both your OLTP workload and your analytics — was going to simplify the data stack. Fewer moving parts, no ETL lag, no synchronization bugs.
In 2026 the reality is more nuanced. HTAP is real and it works, for certain workloads. But the teams chasing "one database to rule them all" on large-scale systems have largely come back to dual-engine designs — with more clarity about why.
What HTAP Actually Promises
HTAP databases (CockroachDB, TiDB, SingleStore, YugabyteDB, and to some extent Spanner) maintain both row-oriented storage for OLTP and columnar storage for analytics, replicating between them internally. The pitch: zero ETL lag, no replica configuration, one SQL dialect.
The genuine wins are real:
- Analytics on fresh data. In a dual-store setup with Kafka or CDC, your analytical layer might be 30 seconds to 5 minutes behind. HTAP internal replication can be sub-second.
- Operational simplicity at small scale. One database means one backup strategy, one security perimeter, one team owning it.
- Dashboards over live operational data. "How many orders are pending right now?" runs against the same store that's processing orders.
These wins are real. The question is what you pay for them.
Where HTAP Breaks Down
Workload isolation is hard. A heavy analytical query — say, a dashboard aggregating 200M rows — competes for CPU and memory with your OLTP transactions. HTAP vendors offer resource groups and query prioritization, but in practice a runaway analytics query at 3am still degrades your P99 write latency. This is not a vendor failure; it's physics.
Columnar performance still lags behind purpose-built OLAP. TiDB's TiFlash and SingleStore's columnstore are good. ClickHouse is faster on pure analytical workloads — sometimes 10–30x for certain scan patterns. If your analytics team is running complex aggregations over billions of rows, the performance gap is not rounding error.
Cost structure. HTAP databases are priced for OLTP reliability (multi-region, synchronous replication). Columnar analytical compute on ClickHouse or Snowflake is priced differently — you pay for compute when you use it. Running analytical workloads on an HTAP cluster means paying OLTP-grade reliability costs for analytical compute.
Schema design tension. OLTP schemas are normalized. Analytical queries prefer denormalized, wide tables. HTAP forces you to choose one canonical schema. The teams that push HTAP hardest end up maintaining materialized views to bridge the gap — which re-introduces ETL, just internal to the database.
The Dual-Engine Reality
Most mature data stacks in 2026 look like this:
Three analytical tiers serving different latency/cost/scale tradeoffs:
- Read replica — milliseconds behind primary, same SQL dialect, free with your existing Postgres setup. Good for operational reports that need fresh data but run simple queries.
- ClickHouse — seconds to minutes lag (via Kafka), purpose-built columnar engine, 10–30x faster than Postgres on scan-heavy queries. Good for product analytics dashboards, real-time aggregations.
- Snowflake / BigQuery / Redshift — minutes to hours lag (batch ETL or streaming), expensive compute but exceptional for complex multi-table joins, ML feature generation, and ad-hoc exploration by data scientists.
You don't need all three. The question is which tiers you actually need.
Read Replicas: Re-Evaluated
Read replicas are underrated. If your analytics queries are simple aggregations over the last 7 days of data and you have fewer than 100M rows in the relevant tables, a Postgres read replica is often the right answer. It's:
- Already in your operational budget
- No ETL to maintain
- Familiar SQL, same schema, same indexes
- Lag under 500ms in normal operation
The failure mode is replica lag spikes. Under heavy write load, replication lag can balloon — a replica that's normally 100ms behind can fall 30–60 seconds behind during a bulk import or migration. If your operational dashboards read from the replica, they silently show stale data.
-- Always check replication lag before relying on replica reads
SELECT
EXTRACT(EPOCH FROM (now() - pg_last_xact_replay_timestamp()))
AS replication_lag_seconds;Add this check to your query middleware. If lag exceeds your threshold (say, 10 seconds for operational reports), route to the primary. This is not exotic — it's standard practice for replica-backed reads.
ClickHouse Over Postgres for Analytics
If you've outgrown read replicas (queries taking >30 seconds, scanning >500M rows, or your DBA is begging you to stop running full table scans on production), ClickHouse is the upgrade path, not Snowflake.
Snowflake is excellent for data warehousing — complex multi-source joins, data science, governed access for many teams. It's expensive per query and optimized for batch workloads with cold-start tolerance.
ClickHouse is optimized for low-latency analytical queries over high-cardinality data. A query that takes 45 seconds on Postgres, 8 seconds on Snowflake, takes 0.4 seconds on ClickHouse. That difference matters for product-facing dashboards and real-time monitoring.
-- ClickHouse: MergeTree for time-series event data
CREATE TABLE events (
event_id UUID,
user_id UInt64,
event_type LowCardinality(String),
occurred_at DateTime,
properties String -- JSON blob
)
ENGINE = MergeTree()
PARTITION BY toYYYYMM(occurred_at)
ORDER BY (occurred_at, user_id)
SETTINGS index_granularity = 8192;
-- Materialized view for pre-aggregated hourly counts
CREATE MATERIALIZED VIEW events_hourly
ENGINE = SummingMergeTree()
PARTITION BY toYYYYMM(hour)
ORDER BY (hour, event_type)
AS SELECT
toStartOfHour(occurred_at) AS hour,
event_type,
count() AS cnt
FROM events
GROUP BY hour, event_type;Key ClickHouse design principles that catch Postgres-trained engineers off guard:
- No primary keys in the relational sense. The
ORDER BYclause inMergeTreeis the primary sort key. Design it around your most common query filters. - Mutations are expensive.
UPDATEandDELETEin ClickHouse are asynchronous background operations. Design your schema to append-only where possible, or useReplacingMergeTreefor upsert semantics. - Columnar means wide tables. Normalize your Postgres schema into wide, denormalized ClickHouse tables. Joins in ClickHouse exist but are not its strength.
Picking Your Architecture
The decision tree is simpler than the vendor landscape suggests:
| Scale | Write Pattern | Analytics Complexity | Recommendation |
|---|---|---|---|
| < 50M rows | Standard OLTP | Simple aggregations | Postgres + read replica |
| 50M–500M rows | Standard OLTP | Product dashboards | Postgres + ClickHouse via CDC |
| 500M+ rows | High-volume writes | Multi-source, BI | Postgres + ClickHouse + Snowflake |
| Regulated, multi-region | OLTP-first | Some analytics | HTAP (TiDB, CockroachDB) |
The HTAP case is real but narrow: when you're in a regulated environment that prohibits data leaving a transactional boundary, or when you genuinely need sub-second analytical freshness and can accept the compute overhead, HTAP earns its complexity.
For everyone else, dual-engine is cheaper to operate, faster on analytics, and easier to scale independently. The ETL lag (30–120 seconds with Kafka+Debezium) is acceptable for 95% of analytics use cases.
The CDC Pipeline: Getting It Right
Kafka with Debezium is the standard CDC pipeline in 2026. The common mistakes:
Not handling schema changes. When you add a column to Postgres, Debezium picks it up automatically. But your ClickHouse consumer needs to handle the new column. Use Kafka Schema Registry with Avro or Protobuf — schema evolution is tracked automatically, and consumers can handle missing fields gracefully.
Ignoring at-least-once delivery. Kafka guarantees at-least-once delivery. Duplicate events in ClickHouse are handled by ReplacingMergeTree (keeps the last version by sort key) or explicit deduplication in your queries. Pick one strategy and be explicit about it.
Not monitoring lag. The Kafka consumer group lag is the metric that tells you how far behind your analytical layer is. Alert on it. A spike in consumer lag means your OLAP layer is serving stale data.
# Debezium Postgres connector config (core settings)
connector.class: io.debezium.connector.postgresql.PostgresConnector
database.hostname: postgres-primary.internal
database.port: 5432
database.dbname: production
plugin.name: pgoutput
publication.name: debezium_pub
slot.name: debezium_slot
table.include.list: public.orders,public.events,public.users
tombstones.on.delete: falseKey Takeaways
- HTAP delivers on its promise for narrow use cases — sub-second analytical freshness, regulated environments, small-to-medium scale — but does not eliminate the performance and cost gaps at large scale.
- Read replicas are underrated: for simple aggregations under 100M rows, they are free, low-latency, and already in your infrastructure.
- ClickHouse wins on low-latency analytical queries over billions of rows where Snowflake's cold-start overhead is unacceptable for product-facing dashboards.
- Dual-engine architectures (Postgres OLTP + ClickHouse/Snowflake OLAP) remain the dominant pattern in 2026 because workload isolation, cost structure, and per-engine optimization each matter at scale.
- CDC via Kafka and Debezium is the standard replication path; treat schema evolution, at-least-once delivery, and consumer lag monitoring as non-optional from day one.
- Choose your analytical tier based on query latency requirements and row count, not vendor marketing: replica for operational reports, ClickHouse for real-time dashboards, Snowflake for complex BI and data science.