Learning Objectives
- Explain the architectural difference between DuckDB's in-process, embedded model and ClickHouse's client-server, distributed model, and why that difference — not raw query speed — is the primary factor in choosing between them
- Evaluate a workload against concrete data-volume, concurrency, and ingestion-rate thresholds to decide whether DuckDB's single-machine model is sufficient or ClickHouse's distributed model is warranted
- Defend a "use both" architecture against the alternative of picking just one, using a real company's documented reasoning
- Recognize when a benchmark comparing the two databases is measuring something narrower than "which database is better," and adjust conclusions accordingly
Why This Matters
DuckDB is the single most common "wait, do we even need ClickHouse" question a team asks itself — and it's a reasonable question, not a naive one. DuckDB's benchmark numbers are genuinely excellent, it requires no server to stand up or operate, and it drops straight into a Python notebook or an application binary with zero infrastructure. An engineer who's only heard "DuckDB is fast and has no ops burden" can talk themselves into replacing a production ClickHouse deployment with it; an engineer who's only heard "ClickHouse is the serious, scalable choice" can end up standing up a whole cluster for a workload one laptop could handle in memory. Both mistakes are expensive, in opposite directions, and this lesson is about the actual axis the decision turns on — which isn't "which one is faster."
The trade-off
The instinct to compare DuckDB and ClickHouse as "which database wins" is the wrong frame from the start. Within DuckDB's sweet spot — data that fits comfortably on one machine — the two are often genuinely comparable in raw speed, and DuckDB sometimes wins outright. The real axis isn't speed; it's deployment model. DuckDB "does not run as a separate process, but completely embedded within a host process" — no server to install, update, or maintain, and for the analytical workloads it targets, "high-speed data transfer to and from the database" as a direct consequence of never crossing a network boundary at all. ClickHouse is a client-server system built from the ground up for the opposite shape of problem: many concurrent users, continuous high-volume ingestion, and more data than any single machine can hold, coordinated across a cluster. Trading one for the other trades away whichever half of that shape your actual workload doesn't need — and the cost of guessing wrong shows up as either needless operational burden or a system that can't do the one thing you actually needed it to do.
Both sides, steelmanned
DuckDB's case. Beyond the no-server simplicity, DuckDB's integration story is unusually tight: "the DuckDB Python package can run queries directly on Pandas data without ever importing or copying any data" — a genuine zero-copy path from a data scientist's existing in-memory dataframe straight into SQL. It's also not a toy at scale: DuckDB has processed "1 billion rows (~50 GB) on a MacBook Pro with 16 GB RAM." And on ClickBench — a benchmark ClickHouse itself created and hosts, not a DuckDB-authored one — DuckDB 1.4 LTS ranks "#1 open-source on ClickBench hot runs, trailing only Umbra, a closed-source research prototype." For a single analyst, a notebook, or an application that wants to embed real analytical SQL without operating a database service, this is a strong, legitimate case.
ClickHouse's case. DuckDB's embedded model is also its hard ceiling: it's "a single process that scales vertically" — one machine, one file, by design, with no built-in path to distribute a query across a cluster or serve many independent concurrent writers coordinating shared state. ClickHouse's whole architecture — everything from Module 3's MergeTree merges to Module 5's replication — exists to serve workloads DuckDB's model structurally can't: sustained ingestion "from Kafka around the clock," "hundreds of concurrent queries" against a shared, continuously-updating dataset, and horizontal scale into the terabyte-to-petabyte range. A customer-facing analytics product serving hundreds of paying users' dashboards simultaneously isn't a data-volume problem DuckDB is bad at — it's a concurrency-and-coordination problem DuckDB's single-process model isn't built to solve at all.
A third option: use both. These two cases aren't necessarily exclusive within one organization, and the strongest real-world answer is often "both, for different jobs" — covered as this lesson's case study below.
Decision framework
Rough, sourced thresholds — not hard cutoffs, but the shape of the reasoning a senior engineer applies:
- How much data, and does it fit on one machine? Under roughly 10GB, DuckDB "often executes analytical queries faster than ClickHouse because it avoids network communication" entirely. The crossover where ClickHouse's distributed execution starts winning "typically occurs between 50GB and 500GB, depending on query patterns," and DuckDB stays viable for "small production workloads under 100 GB." These are data-volume signals, not verdicts — the next two questions matter as much or more.
- How much concurrency does this actually need? DuckDB fits "small production workloads" serving "a few hundred users" in a lighter-weight sense; ClickHouse is the answer once a workload needs "hundreds of concurrent queries" against shared, live data. A dataset can be small in total size and still need ClickHouse if enough independent users are hitting it at once — concurrency, not size, is what breaks a single embedded process.
- How is data arriving? A single-node ingestion path "struggles above 100,000 rows per second"; sustained ingestion beyond that, especially from a continuous stream like Kafka, is squarely ClickHouse's territory, not because DuckDB can't load data quickly in a batch, but because DuckDB has no continuous, concurrent-with-queries ingestion story to match.
- Is this exploratory/embedded, or production-serving? A single analyst's notebook, an ad-hoc join before a meeting, or analytics embedded directly into an application binary is DuckDB's home ground. A dashboard or API that other people depend on being up, current, and responsive under concurrent load is ClickHouse's.
import duckdb
# No connection string, no running server process to reach.
# The engine lives inside this Python process.
duckdb.sql("""
SELECT customer_id, sum(amount) AS total
FROM 'transactions.parquet'
GROUP BY customer_id
ORDER BY total DESC
LIMIT 10
""").show()
There's no ClickHouse equivalent of that snippet that doesn't first involve a server to connect to — a Docker container, a cloud instance, a clickhouse-client pointed at a host and port. That single difference is the entire architectural trade-off this lesson is about, compressed into whether there's a network hop between your query and your data at all.
Common Misconception
"DuckDB beat ClickHouse on ClickBench, so DuckDB is just the faster database — ClickHouse's reputation for speed is outdated."
ClickBench is real and DuckDB's showing on it is a genuine, credible signal — it's ClickHouse's own benchmark, not a DuckDB marketing number. But it tests one shape of workload: "43 queries against a single flat table of roughly 100 million rows," sequentially, with no JOINs. That's close to a best case for a single-machine, in-process engine with no network overhead, and it says very little about hundreds of concurrent users, continuous multi-gigabyte-per-hour ingestion, or a dataset too large for any one machine — exactly the conditions ClickHouse's architecture exists for. A benchmark result is a measurement of the benchmark's workload, not a verdict on "which database is better" in general. The question worth asking about any benchmark before trusting its conclusion is the one this lesson keeps returning to: does the benchmark's shape resemble your actual workload's shape?
Cold Start — No AI
You're a data analyst. Fifty minutes before a meeting, you receive a 40GB Parquet export of last quarter's transaction data and need to run a handful of exploratory joins and aggregations to find a number someone's going to ask about. Nothing is currently deployed — there's no existing ClickHouse cluster or DuckDB setup for this data.
Before reading further or asking an AI assistant anything, decide: would you spin up a ClickHouse instance for this, reach for DuckDB directly against the Parquet file, or something else? Write down the specific properties of this scenario — not general reputation — that drove your answer.
Reflection: Where were you tempted to ask an AI "DuckDB vs ClickHouse, which is better" and take whatever general-purpose answer came back? Would that prompt have surfaced that this specific scenario — one person, one file, a 50-minute deadline, no ongoing concurrent access — is close to the textbook case for DuckDB regardless of which database has a better general reputation, or would it have given you a generic pros-and-cons list that left the actual decision to you anyway?
Reasoning Prompt
A small startup's production analytics dataset is only 8GB total — well under every data-volume threshold this lesson has cited in DuckDB's favor. But it's the backing store for a customer-facing embedded analytics dashboard used by roughly 300 paying customers simultaneously throughout the business day, fed by a Kafka stream continuously ingesting around 500,000 events per minute (roughly 8,300 rows/second sustained, with bursts higher).
A junior engineer proposes DuckDB, citing its size: "8GB is nothing, DuckDB handles a billion rows on a laptop." Reason through whether that recommendation holds up, and what specifically about this scenario the size-based argument is missing. Write your answer before revealing the model answer below.
Model Answer⌄
The junior engineer's data-volume observation is correct and beside the point. 8GB is trivially within DuckDB's range on size alone — but this lesson's decision framework treats data volume as only one of four questions, and this scenario fails the other three in ways that matter more.
Concurrency: 300 simultaneous customers hitting a shared, live dashboard is exactly the "hundreds of concurrent queries" condition that pushes toward ClickHouse, independent of how small the underlying data is. DuckDB's architecture is a single embedded process — it has no native model for many independent client connections concurrently querying (and here, also depending on) the same live dataset the way a client-server system does. Bolting concurrent access onto a single-process, single-file engine isn't a configuration tweak; it fights the model DuckDB is built on.
Ingestion: roughly 8,300 rows/second sustained, continuously, from Kafka, is real but modest against the single-node ceiling this lesson cited ("struggles above 100,000 rows per second") — the volume itself wouldn't rule out a single node. What it does rule out is doing that ingestion concurrently with serving 300 live dashboard readers out of the same embedded process with no separation between writer and readers, which is a coordination problem, not a throughput problem.
Production-serving, not exploratory: this is squarely a system other people depend on being up and current, not one analyst's ad-hoc session — the fourth question in the decision framework, and the clearest signal of all here.
The corrected recommendation: ClickHouse, not because 8GB requires it, but because concurrency and continuous production ingestion against a shared, customer-facing dataset are exactly the conditions this lesson's framework identifies as DuckDB's structural limit, regardless of how small the data is. A learner who initially leaned toward DuckDB because of the size figure hasn't reasoned incorrectly about DuckDB's capabilities — they've weighted the wrong variable as decisive. The size of the data is the least informative of the four questions whenever concurrency or continuous ingestion is also in play.
Case study: PostHog runs both, on purpose
PostHog — an open-source product analytics company — uses ClickHouse and DuckDB simultaneously, for different jobs, rather than treating the choice as an either/or. ClickHouse is "a real-time analytical engine that powers our funnels, trends, retention, and paths over billions of events" — PostHog's core, always-on analytics product. DuckDB was added later specifically to power a managed data warehouse feature: ad-hoc, flexible querying for customers who need broader exploration than PostHog's built-in analytics provide, without PostHog having to horizontally scale infrastructure just for that exploratory access pattern. In their own words, "DuckDB is fantastic at spontaneous, smaller analytical queries as it skips client-server communication and operates on local data," while ClickHouse remains the engine for the sustained, high-volume, concurrently-queried core product. They even built a small piece of glue software, "Duckgres," that "wraps DuckDB in a Postgres server, making it compatible with anything that supports the PostgreSQL wire protocol" — a concrete illustration that "use both" isn't a hand-wave; it's an integration decision a real engineering team invested in on purpose.
Anti-pattern gallery
Standing up a ClickHouse cluster (or ClickHouse Cloud) for a single analyst's ad-hoc exploratory workload. If the data fits on a laptop and only one person needs it for an afternoon, a whole distributed system is pure operational overhead bought for a problem — concurrency, scale, continuous ingestion — that scenario doesn't have. This is the ColdStart scenario's trap, and it's a genuinely common one: reaching for "the serious database" out of habit or reputation rather than because the workload's shape requires it.
Trying to scale DuckDB horizontally to serve growing concurrent production traffic. DuckDB isn't entirely single-process in every configuration — multiple processes can read the same database concurrently in read-only mode, and DuckDB has documented, if narrower, paths for multi-process writes (a beta remote-write protocol, or a DuckLake catalog backed by Postgres for production use). What none of that adds up to is what ClickHouse provides by default: a query scheduler built for hundreds of concurrent queries, with priority, resource quotas, and horizontal read/write scaling across a cluster. Running many independent DuckDB processes behind a load balancer and expecting ClickHouse-shaped concurrency out of it means either reconstructing that coordination layer from scratch on top of DuckDB's narrower primitives, or accepting real limitations (read-only replicas, a single writer, or an added dependency like a Postgres catalog) that a workload's actual concurrency needs may not tolerate. If a workload has outgrown what those documented paths comfortably support, that's the signal to move to a system built for the multi-node, many-concurrent-writer case as a first-class design goal, not a bolted-on extension.
Treating a single benchmark's headline number as the whole answer. ClickBench's 100-million-row, single-flat-table, no-JOIN shape is a real, useful data point — and also not a stand-in for "which database should I use in production." The Misconception above is this anti-pattern's specific, common form.
Summary
DuckDB and ClickHouse aren't competing on raw speed so much as on deployment model: DuckDB is embedded, in-process, single-machine, and effectively zero-ops — genuinely fast, even benchmark-leading, within that footprint. ClickHouse is client-server and distributed, built for concurrent multi-user access, continuous high-volume ingestion, and data too large for one machine. The decision framework runs through data volume (DuckDB is often faster under ~10GB, with a 50–500GB crossover zone), concurrency (DuckDB fits a few hundred light users; ClickHouse is for hundreds of concurrent queries against shared, live data), ingestion rate (single-node ingestion struggles past ~100,000 rows/second, and DuckDB has no concurrent-with-queries continuous ingestion model at all), and whether the workload is exploratory/embedded versus production-serving — with concurrency and ingestion pattern usually mattering more than raw data size alone, as the Reasoning Prompt's 8GB-but-300-concurrent-users scenario shows. PostHog's real architecture — ClickHouse for its core always-on product, DuckDB for ad-hoc customer data-warehouse queries — demonstrates that "use both, for different jobs" is a legitimate, deliberately-engineered answer, not a failure to decide. And any benchmark comparing the two, including ClickHouse's own ClickBench, measures a specific workload shape — a strong result there is real evidence, not a general verdict.
Quiz
1. Why is 'which database is faster, DuckDB or ClickHouse' the wrong central question for choosing between them, according to this lesson?
2. A workload has only 5GB of total data but needs to serve 400 concurrent dashboard users against continuously updating data. Why does the decision framework in this lesson point toward ClickHouse despite the small data size?
3. DuckDB ranks near the top of ClickBench's open-source results. Why does this lesson caution against treating that result as a general verdict that 'DuckDB is faster than ClickHouse'?
4. PostHog uses both ClickHouse and DuckDB in production, for different parts of their product. What does this lesson identify as the actual reason for that split, rather than treating it as an inconsistent or undecided architecture?
5. A single-node ingestion pipeline needs to sustain roughly 250,000 rows per second, continuously, into a table that's simultaneously queried by live dashboards. What does this lesson's decision framework say about this specific combination?